Hola, bienvenid@ a mi sitio personal.

Acá encontrarás algunos textos, proyectos y enlaces en los que he trabajado.

¡Si los encuentras interesante, no dudes en opinar o contactarme!

Proyectos destacados

Actualmente estoy disfrutando desarrollar con la librería streamlit. Algunos proyectos recientes:

En proyectos encontrarás todos los proyectos.

Original

PyCon Latam 2021

Mi tercera PyCon

Tutorial de RISE

Usando jupyter notebooks para presentaciones interactivas

Experiencias del Sprint de Data Umbrella

Lo que aprendí en mi primer sprint contribuyendo a código abierto.

El detective: entre la lógica y el azar

Las raíces de la serendipia y la novela policial clásica en un cuento persa

PyCon Latam 2021 | sebastiandres

PyCon Latam 2021

Mi tercera PyCon

notebook   rise   pycon

Acabo de terminar mi charla en la PyCon Latam 2021, y fue una gran experiencia. A diferencia de otras oportunidades, estaba relajado y disfruté muchísimo dar la charla. Creo que la diferencia tuvo que ver con la preparación. Aunque ya sabía bastante, había aprendido muchísimo más en las últimas semanas preparando el tutorial de RISE que publiqué en el blog. Así que ya tenía todo el material a mano, y fue fácil armar las diapositivas.

En la PyCon utilizamos la plataforma hubilo.com que me gustó mucho, fue fácil de utilizar y no tuve complicaciones durante el evento. Un tremendo esfuerzo del equipo organizador, porque tuvieron que cambiarse a última hora de la plataforma que tenían considerada últimamente.

Algunas de las cosas que aprendí específicamente para esta presentación fueron:

  • Porqué se llama RISE: Reveal.js IPython Slideshow Extension
  • Utilizar _repr_html_ para tener una representación nativa para los jupyter notebook,
  • A ocultar los botones con ,, y a usar header y footer con propiedades de html.
  • A hacer animaciones más complejas con <p class="fragment">Mi texto</p>, al igual que en revealjs
  • A ocultar código con un botón.

La presentación en sus distintas formas de visualización se encuentran acá: Slides pylatam

All systems red | sebastiandres

All systems red

Exploradores planetarios con un twist

lectura

Un libro divertido y fácil de leer. El personaje central es un robot humanoide, y tiene muchas características humanas: humor negro, desprecio por el trabajo, limitaciones de multitasking, procrastinación y adicción a las series televisivas. Me resulta divertida la idea de que la evolución de la tecnología termine produciendo características tan humanas y alejadas de lo perfecto. El nudo central del libro es un poco simplón, pero la narración está muy bien lograda.

Ocultando código en jupyter notebook | sebastiandres

Ocultando código en jupyter notebook

¿Cómo ocultar el código pero no el output en jupyter notebooks?

jupyter   rise

Contenidos

A veces quieres mostrar el resultado de una celda, pero no mostrar el código. Por ejemplo, con el código de un gráfico o de una tabla.

Hay 2 maneras de cumplir con este objetivo:

  1. Simple: creando función auxiliar y ejecutándola.
  2. No tan simple: usando un poco de magia de jupyter notebook.

Opción Simple:

Podemos definir la función en un archivo, y luego importarla y ejecutarla:

from mi_carpeta import mi_script
mi_script.mi_grafico(order=6)

Si es necesario, es posible mostrar el contenido del script o la función:

!cat mi_carpeta/mi_script.py
# Tomado y adaptado desde:
# https://matplotlib.org/stable/gallery/lines_bars_and_markers/fill.html

import numpy as np
import matplotlib.pyplot as plt

def koch_snowflake(order, scale=10):
    """
    Return two lists x, y of point coordinates of the Koch snowflake.

    Parameters
    ----------
    order : int
        The recursion depth.
    scale : float
        The extent of the snowflake (edge length of the base triangle).
    """
    def _koch_snowflake_complex(order):
        if order == 0:
            # initial triangle
            angles = np.array([0, 120, 240]) + 90
            return scale / np.sqrt(3) * np.exp(np.deg2rad(angles) * 1j)
        else:
            ZR = 0.5 - 0.5j * np.sqrt(3) / 3

            p1 = _koch_snowflake_complex(order - 1)  # start points
            p2 = np.roll(p1, shift=-1)  # end points
            dp = p2 - p1  # connection vectors

            new_points = np.empty(len(p1) * 4, dtype=np.complex128)
            new_points[::4] = p1
            new_points[1::4] = p1 + dp / 3
            new_points[2::4] = p1 + dp * ZR
            new_points[3::4] = p1 + dp / 3 * 2
            return new_points

    points = _koch_snowflake_complex(order)
    x, y = points.real, points.imag
    return x, y

def mi_grafico(order=5, figsize=(12,8)):
    """
    Plots the koch snowflake.
    
    Parameters
    ----------
    order : int
        The recursion depth.
    scale : float
        The extent of the snowflake (edge length of the base triangle).
    """
    x, y = koch_snowflake(order=order)
    plt.figure(figsize=figsize)
    plt.axis('equal')
    plt.fill(x, y)
    plt.show()

Opción 2:

Usando una función específica para ocultar código, creada con una mezcla de html y javascript.

def toggle_cell_code(button_id):
    """
    Adds a button to toggle (show/hide) the code cell but not the output. 
    
    Parameters
    ----------
    button_id : str
        An identifier for cells that will hide/show when button is pressed.
    """
    
    from IPython.display import display_html
    my_html = '''
    <button type="button" id="%s" onclick="code_toggle('%s')">Codigo</button>
    <script>
    function code_toggle(my_id) {
        // get the parent element for the cell code and output
        var p = $("#"+my_id);
        if (p.length==0) return;
        while (!p.hasClass("cell")) {
            p = p.parent();
            if (p.prop("tagName") =="body") return;
        }
    // get the cell code and toggle its value
    var cell_code = p.find(".input");
    cell_code.toggle();
    }
    </script>
    ''' %(button_id, button_id)
    return display_html(my_html, raw=True)

Para usar esta funcionalidad, basta con incluirla en la celda del código (antes o después), para agregar el botón que permitirá ocultar y mostrar el código.

toggle_cell_code("un_string_unico_y_reconocible")

# Código para graficar
import numpy as np
import matplotlib.pyplot as plt

# Fixing random state for reproducibility
np.random.seed(19680801)


N = 50
x = np.random.rand(N)
y = np.random.rand(N)
colors = np.random.rand(N)
area = (30 * np.random.rand(N))**2  # 0 to 15 point radii

plt.scatter(x, y, s=area, c=colors, alpha=0.5)
plt.show()

Así se ve:

¿Cómo funciona?

Analicemos el código en python que ejecuta la función toggle_cell_code:

Primero:

def toggle_cell_code(button_id):

La función toma un único argumento: button_id.

Segundo:

from IPython.display import display_html

Tercero:

my_html = '''
    Mucho código en html acá que analizaremos después...
    ''' %(button_id, button_id)

Este código define la variable my_html, en la cual se inserta 2 veces el valor button_id.

Cuarto:

return display_html(my_html, raw=True)

Finalmente, se regresa como el display del html creado mediante una función de IPython.display.

Analicemos ahora el código html:

Primero:

<button type="button" id="%s" onclick="code_toggle('%s')">Codigo</button>

El código anterior define un botón, con un identificador único (button_id) cuyo valor se define de manera dinámica (en el momento de ejecutar la celda del notebook). Al apretar el botón ejecuta la función code_toggle() con el mismo identificador (button_id) como argumento. Eso permite que la función en javascript sepa cuál es la celda en específico que debe ocultar.

Segundo:

<script>
    function code_toggle(my_id) {
        // get the parent element for the cell code and output
        var p = $("#"+my_id);
        if (p.length==0) return;
        while (!p.hasClass("cell")) {
            p = p.parent();
            if (p.prop("tagName") =="body") return;
        }
    // get the cell code and toggle its value
    var cell_code = p.find(".input");
    cell_code.toggle();
    }
    </script>

Primero que nada, $ (jquery) permite encontrar los elementos en una página web, en este caso, el elemento asociado al identificador entregado (button_id). Luego se itera hasta encontrar el elemento que contiene la celda de código y el output (se identifica porque tiene clase "cell"). Luego se accede a la celda de código (que siempre la clase "input"). Finalmente, toggle muestra/oculta el botón.

Nota:

  • Los identificadores tienen el caracter especial #
  • Las clases tienen el carácter especial .

Todas estas ideas son una compilación y simplificación de códigos de muchas otras personas. Algunos enlaces relevantes son:

Agregando un filtro en fastpages | sebastiandres

Agregando un filtro en fastpages

Más personalización en fastpages 2

fastpages   css   javascript

El menú superior del blog empezaba a tener demasiadas opciones, y tenía ganas de simplificarlo. Me dí cuenta que en realidad habían 2 grandes temáticas: lo que escribo y lo que leo. Lo que estoy leyendo tiene siempre el mismo formato: opinión de libro. Lo que escribo puede ser un post simple (en markdown) o algo más sofisticado en un notebok, o bien ser algún escrito de ficción. Lo que necesitaba era agregar botones con filtros para poder seleccionar según fuera necesario.

Antes de ponerme a modificar el html de la página web, hice una página web en local con html y css autocontenido, que cumpliera con estas condiciones. Después de googlear un poco, aprendí que eso podía hacerse de manera simple con las opciones display: 'none' y display: 'block'.

El código es el siguiente. Toda la magia está en usar javascript, donde la función querySelectorAll que permite encontrar todos los elementos de una cierta condición (las clases story, notebook y post). Para manipular más fácil creé una función que muestra una(s) clase(s) y esconde otra(s).

<!DOCTYPE html>
<html>
 
<head>
    <style>
        .story {background-color: green;}
        .notebook {background-color: blue;}
        .post {background-color: red;}
    </style>
</head>
 
<body>
    <button onclick="show_hide('.story','.notebook, .post')">Show Book</button> 
    <button onclick="show_hide('.notebook','.story, .post')">Show Notebooks</button>
    <button onclick="show_hide('.post','.notebook, .story')">Show Posts</button>
    <button onclick="show_hide('.story,.notebook, .post','')">Show All</button>
 
    <div class="story"> <h1>Book</h1> </div>
    <div class="notebook"> <h1>Notebook</h1> </div>
    <div class="post"> <h1>Post</h1> </div>
    <div class="notebook"> <h1>Notebook</h1> </div>
    <div class="post"> <h1>Post</h1> </div>
    <div class="story"> <h1>Book</h1> </div>
    <div class="notebook"> <h1>Notebook</h1> </div>
      
    <script type="text/javascript">
        function show_hide(selector_show, selector_hide) {
            if (selector_show.length>0){
                elements = document.querySelectorAll(selector_show);
                for (let i = 0; i < elements.length; i++){
                    elements[i].style.display = 'block';
                }
            }
            if (selector_hide.length>0){
                elements = document.querySelectorAll(selector_hide);
                for (let i = 0; i < elements.length; i++){
                    elements[i].style.display = 'none';
                }
            }    
        }
    </script>
</body>
</html>

¡Listo! Una vez que estaba funcionarlo, solo fue necesario agregarlo al layout correspondiente, creando las clases respectivas para cada tipo de post. ¡Me sorprendió que funcionó a la primera!

Como los botones se veian un poco feos, les puse el css de un botón de streamlit.

.css-qbe2hs {
    justify-content: center;
    padding: 0.25rem 0.75rem;
    border-radius: 0.25rem;      
    @media (orientation: portrait) {
       font-size: 75% !important;
    }
    margin-bottom: 10px !important;
    margin-right: 10px !important;
    line-height: 1.6;
    color: inherit;
    width: auto;
    background-color: rgb(255, 255, 255);
    border: 1px solid rgba(38, 39, 48, 0.2);
}

Finalmente, el resultado se ve muy profesional para lo sencillo del código:

Cambiando fontsize de código | sebastiandres

Cambiando fontsize de código

Más personalización en fastpages

fastpages   css

La configuración por defecto de fastpages para el código no es de mi agrado. Me molestaba que incluso cuando la línea no es muy larga se ocultaba gran parte del código.

Esto ocurre porque el tamaño de la fuente es mayor que la del texto. Al mirar el css, se ve que los textos de código están definidos como el 105% o 110% del tamaño normal de la fuente. Yo pensaría que debe ser al revés: el código está para ser mirado pero no analizado en profundidad.
Si lo desea, alguien descargará el código y lo revisará en su editor de peferencia. Es mejor que se vea todo el código, a que haya que hacer scroll.

¿Qué tan difícil es el cambio?

Googleando llegué a la siguiente respuesta donde se explica que es necesario editar el archivo _sass/minima/custom-styles.scss.

Agregué el siguiente código, para reducir el tamaño de la fuente al 75%.

code {
    font-size: 75% !important;
}

pre {
    font-size: 75% !important;
}

He visto que !important se desaconseja, pero bueno, podremos hacer una excepción en virtud del tiempo y de la humildad de este blog.

Test final: el siguiente texto con 80 chars debería verse completo…

123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 

¡Funciona!

Videos en Jupyter Notebook | sebastiandres

Videos en Jupyter Notebook

¿Cómo incrustar videos en markdown o jupyter notebook?

jupyter   rise

Poner un video en markdown o jupyter notebook es sencillo, pero hay varias opciones y no todas funcionan. ¡La opción que pongo acá funciona en todos los casos!

 ¿Cómo se ponen videos en un notebook?

Es posible usar un video en local (de tu disco duro) o en línea. Sin embargo, resulta aconsejable subir el video en línea y enlazar ese video. ¿Por qué? Porque así la ruta no cambia al ejecutarlo como notebook en local y al convertirlo mediante fastpages en página web.

Usaremos el video que usan de ejemplo en w3school: mov_bbb.mp4

Agregando el repositorio en github, obtenemos la ruta del archivo (notar que tiene que estar en version raw):

https://github.com/sebastiandres/blog/blob/master/videos/mov_bbb.mp4?raw=true

El código html para incrustar un video en html o markdown es:

<video controls src="path_to/video_name.mp4" width="800"></video>

Podemos usar el html en una celda de markdown:

También es posible usar código en python para incrustar el video como resultado de la ejecución de una celda:

from IPython.display import HTML

HTML("""
<video controls src="https://github.com/sebastiandres/blog/blob/master/videos/mov_bbb.mp4?raw=true" width="800">
</video>""")

Todas las opciones del tag <video> están en la documentación.

Tutorial de RISE | sebastiandres

Tutorial de RISE

Usando jupyter notebooks para presentaciones interactivas

jupyter   rise

Creo que existe poco material sobre cómo utilizar la librería RISE, especialmente en español. En esta serie de artículos dedicados espero poder contribuir a disminuir esa brecha.

Si encuentras errores o existe alguna funcionalidad no documentada, ¡no dudes en comentar!

 ¿Qué es RISE?

RISE es una extensión a los jupyter notebooks que permite transformarlos en una presentación interactiva. Toda las celdas pueden editarse y ejecutarse directamente, durante la presentación. Esto es práctico si necesitas corregir un error en una celda de texto. Más importante aún, puedes ejecutar código directamente en el kernel. En una misma diapositiva puedes tener múltiples celdas y elegir cuál ejecutar, o corregir el texto y volver a ejecutar.

 ¿Porqué es mejor?

Primero, porque simplifica enormemente la generación de material. El jupyter notebook es simultáneamente la presentación, el código, el apunte y texto oficial, y no necesitas actualizar en múltiples lugares. Detesto sacar una captura de pantalla a un trozo de código, porque tendré que estar eternamente actualizando esa imagen. También odio escribir código en cuadros de texto que quedan sin formato y donde siempre se pasa algún error, que no se detecta porque el código no se puede ejecutar.

Segundo, porque simplifica la distribución del material. La presentación contiene todo lo que la audiencia necesita: explicaciones, imágenes, videos y/o código. Proporcionando un archivo requirements.txt es fácil recrear un ambiente con las librerías necesarias para ejecutar el código. Almacenando el jupyter notebook en github, es además fácil compartir el archivo ipynb del jupyter notebook, un html o pdf pre-generado fácil de visualizar o incluso un enlace a un repositorio binder donde la presentación se puede volver a visualizar de manera interactiva. Entregar múltiples opciones le entrega facilidades a la audiencia y permite generar más impacto.

 ¿Qué desventajas tiene?

La principal desventaja es que actualmente RISE funciona únicamente con Jupyter Notebook.

No funciona con Jupyter Lab (aún) ni con Google Colaboratory (probablemente nunca).

Sobre el tutorial

Como el jupyter notebook se transforma en una presentación, se heredan todas las funcionalidades, tanto de texto (markdown, latex) como de código (gráficos, display, widgets) del formato jupyter notebook. Probablemente ya conoces muchas de estas funcionalidades que no son de RISE. En el enlace podrás encontrar estas funcionalidades de jupyter notebook útiles para presentaciones interactivas.

En este tutorial se divide en tres partes:

  • Aspectos básico: Cómo instalar la librería, cómo crear una presentación y cómo realizar una presentación exitosa.
  • Aspectos intermedios: Cómo configurar el diseño en RISE y cómo compartir tu presentación.
  • Aspectos avanzados: Cómo crear columnas, cómo realizar encuestas, cómo portar una presentación de powerpoint a jupyter notebook y otros consejos.

Enlaces:

Nuevo favicon | sebastiandres

Nuevo favicon

De python a sebastiandres en 1 paso.

fastpages   favicon

Ya había reemplazado el favicon del sitio, pero no me terminaba de convencer. Hoy mirando el logo de python me di cuenta que podía facilmente transformarse en una S, que me pareció muy adecuado para un favicon simple pero memorable.

La imagen final tiene algunos detalles en los bordes, pero no es relevante dado que al convertirlo a favicon se reduce enormemente el tamaño.

Hay varios servicios para generar un favicon a partir de una imagen, en este caso obtuve mejores resultados con favicon.io.

El favicon se puede modificar reemplazando directamente images/favicon.ico, o bien, editando el archivo _includes/favicons.html para definir una ruta diferente.

Tutorial de RISE - parte 3 | sebastiandres

Tutorial de RISE - parte 3

Aspectos avanzados para hacer presentaciones interactivas con jupyter notebooks

jupyter   rise

Contenidos

¿Cómo puedo configurar texto y columnas?

Como las celdas de markdown aceptan html, es posible usar todos sus trucos. Uno de éstos permite generar dos columnas, típicamente para tener una columna con texto y otra con una imagen (estática o gif).

Por ejemplo, el siguiente código en html se puede usar en celdas de markdown

<div>
    <div style="display: inline-block;  width: 40%;">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </div>
    <div style="display: inline-block; vertical-align: text-top; width: 50%;">
    <img src="2021-08-03-tutorial-rise-parte-3/columnas.gif" alt="LoreIpsum">
    </div>
</div>

y proporciona la siguiente imagen:

Pero en general, hay que segur el consejo: keep it simple.

Gastar 1 hora en una slide de 1 minuto es una mala inversión.

Convierte tus presentaciones automáticamente

Uno de los primeros desafíos para comenzar a usar notebook + rise es convertir el contenido de las presentaciones que ya están en powerpoint. Para solucionar eso, creé un sitio que permite convertir una presentación powerpoint a jupyter notebook + rise de manera automática. Ya genera texto e imagen a 2 columnas, como es el caso habitual en diapositivas. No es perfecto, pero te ahorrará un buen tiempo de copiar y pegar texto.

La aplicación para convertir se encuentra en este enlace: pptx a rise

El funcionamiento es el siguiente. Supongamos que tienes una presentación que necesitas convertir: simple.pptx

Primero que nada, en el sitio debes cargar la presentación, esperar que se convierta y descargar el notebook + RISE equivalente:

Después de descargar y descomprimir, al lanzar el notebook en jupyter la presentación debería comenzar de inmediato:

Observación 1: El notebook que se obtiene es el punto de partida para editar la presentación. Puedes agregarle más contenido y código, y personalizar el diseño tanto como desees.

Observación 2: Si no lo has hecho antes, deberás instalar jupyter notebook y RISE, obviamente.

Ocultando código

A veces quieres ejecutar una celda pero no mostrar largas filas de código.

Hay 2 formas de hacerlo:

  1. Simple: creando función auxiliar y ejecutándola.
  2. No tan simple: usando un poco de magia de jupyter notebook.

Opción Simple:

Podemos definir la función en un archivo, y luego importarla y ejecutarla:

from mi_carpeta import mi_script
mi_script.mi_grafico(order=6, figsize=(12,8))

Opción 2:

Usando una función específica para ocultar código, creada con una mezcla de html y javascript.

def toggle_cell_code(button_id):
    """
    Adds a button to toggle (show/hide) the code cell but not the output. 
    
    Parameters
    ----------
    button_id : str
        An identifier for cells that will hide/show when button is pressed.
    """
    
    from IPython.display import display_html
    my_html = '''
    <button type="button" id="%s" onclick="code_toggle('%s')">Codigo</button>
    <script>
    function code_toggle(my_id) {
        // get the parent element for the cell code and output
        var p = $("#"+my_id);
        if (p.length==0) return;
        while (!p.hasClass("cell")) {
            p = p.parent();
            if (p.prop("tagName") =="body") return;
        }
    // get the cell code and toggle its value
    var cell_code = p.find(".input");
    cell_code.toggle();
    }
    </script>
    ''' %(button_id, button_id)
    return display_html(my_html, raw=True)

Y así se ve en el código:

toggle_cell_code("un_string_unico_y_reconocible")

# Código para graficar
import numpy as np
import matplotlib.pyplot as plt

# Fixing random state for reproducibility
np.random.seed(19680801)


N = 50
x = np.random.rand(N)
y = np.random.rand(N)
colors = np.random.rand(N)
area = (30 * np.random.rand(N))**2  # 0 to 15 point radii

plt.scatter(x, y, s=area, c=colors, alpha=0.5)
plt.show()

Así se ve:

hide_code()
print("Hmmm444")
886216a64d679f3b
Hmmm444

¿Cómo puedo hacer actividades interactivas?

Para incluir actividades interactivas, como las que se pueden hacer con https://www.mentimeter.com/, https://kahoot.com/ o https://quizizz.com/ (entre otros), es posible tomar varios caminos.

Lo más simple es incluir una diapositiva con el enlace al que tiene que acceder los usuarios, idealmente con un código QR para facilitar el acceso desde smartphones. Luego simplemente te cambias a la página correspondiente para mirar los resultados y compartirlos en pantalla.

Otra opción es no salir del jupyter notebook e incrustar la página web de los resultados. Para eso, es posible usar IFrames en una celda de código. De esa manera, es posible embeber dentro del notebook y una diapositiva una página web con una actividad.

from IPython.display import IFrame
IFrame("https://es.wikipedia.org/wiki/Iframe", width=600, height=450)

Importante:

  • Comparte un enlace y código QR del url al que debe acceder tu audiencia.
  • En una diapositiva, mediante IFrame, muestra lo que tu audiencia debería ver en sus celulares o computadores.
  • En otra diapositiva, mediante otro IFrame, coloca el resultado de la actividad interactiva que debería irse completando a medida que la gente responda.

Ahora bien, puede suceder que la página que desees incrustar con IFrames no lo permita, así que siempre realiza pruebas antes de tu presentación.

¿Cómo hacer encuestas?

Es muy importante recibir retroalimentación de la presentación para poder mejorar.

Recomiendo hacer 3 preguntas simples al finalizar toda presentación:

  • ¿Qué te pareció la charla? Evaluar de 1 a 5.
  • ¿Qué le agregarías a la charla?
  • ¿Qué le sacaríias a la charla?

Esto puedes lograrlo embebiendo una encuesta de Google Forms, Microsoft Forms u otros mediante un IFrame.

from IPython.display import IFrame
IFrame("https://forms.office.com/Pages/ResponsePage.aspx?id=zu7OdUTRPU-clJ5rQCX8_4qs5cX1Y7dFhVdiCz848sBUOTVJWktZQlBVNVJDNTg4OVA1N1JaMVlSNS4u", 
       width=450, height=600)

Usa un teclado inalámbrico

En charlas y clases resulta cómodo tener un teclado y mouse inalámbrico. Esto permite que no tengas que estar cerca del notebook, de esa manera puedas moverte libremente por el escenario.

Resulta práctico también para crear la posibilidad que un asistente de la charla pueda editar una celda de código. ¡Esto es especialmente entretenido en una clase!

Tutorial de RISE - parte 2 | sebastiandres

Tutorial de RISE - parte 2

Aspectos intermedios para hacer presentaciones interactivas con jupyter notebooks

jupyter   rise

Contenidos

¿Cómo puedo compartir la presentación?

Compartir la presentación suele ser uno de los pasos más importantes y sencillos. Existen muchas opciones: compartir el código en un repositorio, mediante mybinder de manera completamente interactiva, como html estático o como pdf.

Repositorio

No trabajes en local. Siempre es más conveniente ir versionando los archivos mediante git en un repositorio. Ese repositorio puede respaldarse en línea, en un proveedor como github, bitbucket, gitlab o tu u otro de tu preferencia. El proveedor github es una buena opción gratuita que tiene muchas integraciones y complementos.

Asegúrate de tener un archivo requirements.txt que contenga las librerías que se requieren para poder ejecutar las distintas celdas de código de tu presentación. No olvides incluir todo el código, datos e imágenes.

Resulta útil tener un archivo README.md con la descripción del repositorio, y enlaces a las distintas opciones de visulización para el archivo.

En github, para un usuario nombre_usuario en un repo nombre_repo, puedes además crear una página index.html , y de esa manera al activar "Gitub Pages" la página estará accesible en https://nombre_usuario.github.io/nombre_repo/. De manera más general, un archivo en la ruta /ruta_carpetas/nombre_archivo.html se visualizará en https://nombre_usuario.github.io/nombre_repo/ruta_carpetas/nombre_archivo.html.

Yo he diseñado un template con un formato fijo que reutilizo para las distintas presentaciones, y que así se ve para una de mis charlas (Pycon Colombia 2020):

Creé el formato para para emular a linktree, de manera de reutilizarlo con mínimo esfuerzo. Escribí al respecto en este post.

mybinder

Binder es un servicio que permite lanzar un jupyter notebook en el navegador y en la nube, sin consumir recursos de tu computador y de manera completamente gratuita. Resulta práctico para que otras personas pueden ver la presentación de manera interactiva sin tener que instalar nada en su computador, ¡incluso desde un smartphone o tablet!

Para usar mybinder, debes tener tu repositorio en github.

En Binder puedes introducir los distintos parámetros:

  • Usuario github: nombre_usuario
  • Nombre repositorio: nombre_repositorio
  • Nombre de la rama del repositorio: nombre_rama
  • Ruta al archivo: ruta_carpetas
  • Nombre del archivo: nombre_archivo.ipynb
  • En la rama, en el directorio principal, debe existir el archivo requirements.txt

Al acceder a la siguiente página se ejecutará el jupyter notebook, incluyendo la extensión RISE si la colocaste en los requirements.txt:

https://mybinder.org/v2/gh/nombre_usuario/nombre_repositorio/nombre_rama?filepath=ruta_carpetas/nombre_archivo.ipynb

Exporta a html

Para generar un html funcional de las diapositivas, tienes que hacer en el terminal:

jupyter nbconvert --to slides ruta_carpeta/nombre_archivo.ipynb

donde ruta_carpeta/nombre_archivo es, por supuesto, la ruta a tu notebook-presentación.

Se generará un archivo html estático de tu presentación, con el nombre ruta_carpeta/nombre_archivo.slides.html. Esta página web en html puedes visualizarse en el navegador sin mayores dependencias, pero donde no podrás editar o ejecutar las celdas. Si ya realisaste tu presentación, puedes guardarla en el repositorio como un respaldo adicional.

Exporta a pdf

Para generar la presentación en pdf se requieren 3 pasos:

  1. Genera las diapositivas en html, usando nbconvert, para que queden "activas":

    jupyter nbconvert --to slides ruta_carpeta/nombre_archivo.ipynb --post serve

    Se abrirá una página web en el navegador, en la dirección http://127.0.0.1:8000/nombre_archivo.slides.html#/

  2. Edita la ruta, para que sea http://127.0.0.1:8000/nombre_archivo.slides.html?print-pdf. Nota que hay que reemplazar #/ por ?print-pdf. La página mostrará ahora las diapositivas de manera vertical.

  3. Finalmente, guarda la página web como pdf. Puedes jugar con distintas opciones según necesites (sin márgenes, activar imágenes de fondo, etc.)

La documentación oficial para exportar a pdf está acá.

¿Cómo configuro el diseño de RISE?

General

Todas las configuraciones de RISE se realizan editando la metadata del notebook. Para ello, tienes que ir a Edit/Edit notebook metadata y agregar la(s) opcion(es) deseadas.

Metadata

La metadata es un diccionario json con las distintas opciones de todo el notebook. Por defecto, no trae ninguna opción cargada de RISE. Por eso, tenemos que agregar las opciones que se quieren configurar:

"rise": {
             "opcion_1": valor_opcion_1,
                      ...
             "opcion_n": valor_opcion_n,
         }
`

Observación importante:

Cada vez que editas la metadata, es necesario cerrar (apagar) el notebook y volver a abrirlo para que se recarguen las opciones.

Lanzar RISE de manera automática

Permite que al activar un jupyter notebook, este se ejecute en modo presentación de manera automática. ¡Opción ampliamente recomendada!

En la metadata del notebook se agrega la opción de "autolaunch" con valor true (todo en minúsculas, ¡es verdadero de javascript, no de Python!):

"rise": {"autolaunch": true}
`

De esta manera, si publicas tu jupyter notebook con mybinder.org, se mostrará en modo presentación automáticamente (audiencia no necesitará saber donde está el botón de “iniciar presentación", por ejemplo si lo compartes en mybinder).

Metadata

Eligiendo un tema

El tema de la presentación controla el aspecto general de la presentación.

En la metadata del notebook se agrega la opción de "theme" con el nombre del tema deseado:

"rise": {"theme": "sky"}
`

Existen 11 opciones, que se heredan de la librería revealjs:

  • black: Fondo negro, letras blancas, links azules.
  • white: Fondo blanco, letras negras, links celestes.
  • league: Fondo gris, letras blancas, links celestes.
  • sky: Fondo celeste, letras oscuras, links azules.
  • beige: Fondo beige, letras oscuras, links cafés.
  • simple: Fondo blanco, letras negras, links azules.
  • serif: Fondo café, letras grises, links cafés.
  • night: Fondo negro, letras blancas, links naranjos.
  • blood: Fondo oscuro, letras blancas, links rojos.
  • moon: Fondo azul oscuro, letras grises, links azules.
  • solarized: Fondo blanco crema, letras verde oscuro, links azules.

Se pueden comparar los temas en la siguente imagen:

Themes

La imagen fue compilada por el siguiente blog.

Eligiendo una transición

El tipo de transición entre slides se define de manera similar. En la metadata del notebook se agrega la opción de "transition" con el nombre de la transición deseada:

"rise": {"transition": "zoom"}

Las opciones, heredadas de las transiciones existentes en la librería revealjs, son:

  • none: Sin animación.
  • fade: Animar con Cross fade — default for background transitions
  • slide: Slide between backgrounds — default for slide transitions
  • convex: Animar con ángulo convexo.
  • concave: Animar con ángulo convexo.
  • zoom: Animar para que la diapositiva siguiente crezca desde el centro de la pantalla.

El siguiente gif muestra cada transición:

Transitions

Definiendo una imagen de fondos

Simplemente, usar:

"rise": {
     "backimage": "mybackimage.png",
 }
}

Esta opción es útil si quieres un fondo personalizado, como a veces se exige en algunos congresos. Como es habitual al manejar imágenes, conviene usar archivos png con fondo transparente para que se integre bien con el theme elegido.

Definiendo header y footer:

Simplemente, usar:

"rise": {
     "header": "<h1>Hola</h1>",
     "footer": "<h3>Mundo</h3>"
 }
}

Puedes usar <br> y &nbsp; para introducir saltos de línea y espacios en blanco, respectivamente.

¿Cómo activo la pizarra?

Es posible activar una pizarra (chalkboard) para realizar anotaciones durante la presentación.

Para eso, en Edit/Edit Notebook Metadata, es necesario agregar el siguiente texto a la metadata del jupyter notebook:

{
 ...
 "rise": {"enable_chalkboard": true}
}

Durante la presentación se puede utilizar la pizarra haciendo click en los íconos, que permiten dibujar en una pizarra o sobre las diapositivas.

Es posible cambiar de color de lapiz presionando s y q. Se puede borrar la pizarra con -.

Las anotaciones se preservan incluso al cerrar el modo presentación. Sin embargo, no se guardan al cerrar el notebook.

Importante consejo final

Tantas opciones pueden ser sobrecogedoras y difíciles de recordar. Como no se puede tener comentarios en un json, resulta práctico tener guardadas todas las opciones y simplemente agregar un _ antes de las opciones que no se desean usar.

En mi caso, tengo el siguiente json en todos mis notebooks con lo cual no tengo que memorizar nada, y sólo adapto según necesito.

"rise": {
        "autolaunch": true,
        "enable_chalkboard": true
        "theme": "black",
        "transition": "zoom",
        "_header": "<h1>PRESENTATION NAME // EVENTs</h1>",
        "_footer": "<h3>NAME, DATE</h3>",
        "_backimage": "path/to/mybackimage.png",
        "_theme_options": [ "black", "white", "league", "beige", 
                           "sky", "night", "serif"], 
        "_transition_options": ["none", "fade", "slide", "convex", "concave", "zoom"],
        }

Tutorial de RISE - parte 1 | sebastiandres

Tutorial de RISE - parte 1

Aspectos básicos para hacer presentaciones interactivas con jupyter notebooks

jupyter   rise

Contenidos

 ¿Qué es RISE?

RISE es una extensión a jupyter notebook que, en lugar de desplegar las celdas en una larga página web, las despliega en una presentación usando la librería de javascript reveal.js.

Sigue siendo una página web (al igual que el notebook), pero las celdas se agrupan en diapositivas.

¿Qué contenido puedo poner?

Puedes mezclar contenido usando las celdas de markdown y código, según necesites:

  • Markdown: texto, latex, imágenes, tablas, etc.
  • Código: código, gráficos simples o interactivos, videos, sonido, iframes, javascript, entre otros.

Como regla general, si se muestra correctamente en el notebook, se verá bien en la diapositiva. ¡Sé creativo!

 ¿Cómo instalar?

La instalación de la extensión RISE es extremadamente fácil. Basta con usar pip o conda:

pip install rise

o

conda install -c conda-forge rise

Eso hará que se agregue el botón de iniciar presentación (destacado en rojo).

También puedes agregar rise a tu archivo requirements.txt para que se instale automáticamente al generar un ambiente.

En caso de tener problemas, revisa los detalles adicionales de instalación.

¿Cómo configurar lo que está en cada diapositivas?

Paciencia, se requiere todavía un paso adicional.

En el menú de jupyter notebook, es necesario seleccionar View/Cell Toolbar/Slideshow para que permita configurar el tipo de celda para diapositiva.

Esto se requiere porque a cada celda del notebook se le agregará metadata para saber en que diapositiva debe ir (o si se debe saltar).

Eso dejará todo configurado para poder seleccionar el tipo de celda respecto a la diapositiva.

 ¿Cómo moverse por las slides?

Al hacer click en el botón "Iniciar presentación", la presentación se iniciará en la celda que esté activa.

  • Se accede a la próxima diapositiva o fragmento con Espacio (o la flecha derecha).
  • Se retrocede a la diapositiva o fragmento anterior con Shift Espacio (o la flecha izquierda).
  • Se avanza a la proxima sub-diapositiva con Page Up.
  • Se retrocede la sub-diapositiva anterior con Page Down.

Una diferencia de una presentación típica de PowerPoint es que existen 2 dimensiones: las diapositivas (slides) que avanzan de izquierda a derecha como es tradicional, pero también sub-diapositivas (subslides) que son slides opcionales y que avanzan de arriba a abajo.

Observación: En general, es dificil recordar el orden de las slides y sub-slides. Yo personalmente nunca uso sub-slides por esta razón y prefiero solo tener orden "horizontal".

¿Cómo se configuran las diapositivas?

Existen varios tipos de celda con distintas funcionalidades:

  • -: valor por defecto. La celda se muestra con la slide anterior.
  • Slide: inicia una nueva diapositiva (dirección horizontal).
  • Sub-slide: iniciar una nueva sub-diapositiva (dirección vertical).
  • Fragment: se concatena a la celda anterior, pero no se muestra inmediatamente.
  • Skip: no se muestra la celda en las diapositivas.
  • Notes: No se muestra en las diapositivas, sólo se muestra en las notas para el presentador.

¿Qué opciones hay?

Existen múltiples funcionalides accesibles con el teclado durante la presentación, pero las principales a recordar son:

  • ?: ver todos los shortcuts.
  • ,: ocultar los botones.
  • \: poner la pantalla en negro. Útil para discutir algo sin distracciones visuales.
  • ``:

Las funcionalidades se controlan con los siguientes botones:

¿Se puede editar durante la presentación?

¡Sí!

Es posible editar y ejecutar las celdas de markdown y de código durante la presentación.

Se usa el mismo sistema de doble click para acceder a modo edición, y Alt Enter para ejecutarla.

¿Cómo controlo el tamaño?

Un problema común es que al conectar el computador a otra pantalla o datashow, no se alcanza a ver en la diapositiva todo el código, texto o imagen.

Lo único que debes hacer es usar usar Ctrl + y Ctrl - para regular el tamaño (Command +y Command - en Mac), de la misma manera que regulas el tamaño de una página web.

 ¿Dónde están las notas del presentador?

Puedes abrir las notas del presentador presionando t.

Para poder usar las notas del presentador necesitas tener al menos 2 pantallas: una pública para compartir y otra para mantener privada.

Comparte tu estructura

Cuando se ejecuta código en una presentación, es una buena práctica mostrar la estructura de carpetas y los archivos con los que se va a trabajar. Eso ayudará a despejar dudas de cómo está funcionando el código. Esto se puede lograr fácilmente con comando mágicos (como %ls) o ejecutando código en bash (como !ls). La diferencia entre ambos radica en lo siguiente:

  • Los comandos mágicos son específicos y definidos por cada kernel. Pueden existir comandos que estén en python pero no en R.
  • Los ! permiten ejecutar instrucciones en el terminal y es más flexible. Uno de los más comunes es ofrecer instalar las librerías, como:
    pip install rise matplotlib
    

Gráficos

Para mostrar gráficos resulta práctico que no se genere una ventana adicional, sino que se agreguen a la celda de resultados. Esta es una práctica común en jupyter notebook/lab, pero es más importante aún al pensar en las diapositivas.

%matplotlib inline
from matplotlib import pyplot as plt
import numpy as np

with plt.xkcd():
    fig = plt.figure(figsize=(14,6))
    x = np.linspace(-5,5,num=100)
    y = np.abs(np.abs(np.sin(2*x)/x))
    plt.plot(x,y)

Apoyos gráficos

Me ha servido mucho insertar gifs en las presentaciones. Un buen gif animado es un buen compromiso entre una imagen y una película, y sirve para tener una animación que ilustre algun proceso. Existen muchos programas para grabar gifs. Una solución que me ha funcionado bien para animar gifs simples es crear un diagrama mediante una animación en PowerPoint, y después grabar un gif considerando apropiadamente los tiempos.

Tutorial de RISE - Aspectos preliminares | sebastiandres

Tutorial de RISE - Aspectos preliminares

Funcionalidades de jupyter notebooks útiles para presentaciones interactivas

jupyter   rise

Nota: Este documento es el preámbulo de la entrega por partes del tutorial de RISE.

APUNTES:

  • SONIDO
  • VIDEO
  • Widgets

Lo avanzado

IPython.Display

Audio Video HTML repl

Lo avanzado

Ipywidgets

Matpllotlib widgets

Uniendo archivos Excel | sebastiandres

Uniendo archivos Excel

Automatizando unir archivos históricos

jupyter   rise

Contexto

Hoy me pidieron unir varios archivos excel en un único archivo. Tomé las siguientes convenciones:

  • Los archivos excel están en la carpeta in, y tienen las mismas columnas en el mismo orden.
  • La carpeta out tiene contendrá 2 archivos: un arhivo excel con la concatenación de todos los archivos, y una versión donde se mantuvo un único registro por rut (el último).

Solución

Pandas

import pandas as pd
from glob import glob

# Leer todos los archivos excel de la carpeta in
all_files = glob("in/*.xlsx")

# Get columns
df = pd.read_excel(all_files[0])
columns = df.columns

# Unir los archivos en una lista
df_list = []
cols_list = []
for in_file in all_files:
    print(in_file)
    df = pd.read_excel(in_file)
    df.columns = columns # Renombrar las columnas, mantener la convención del primer archivo
    df["Archivo"] = in_file
    df["#Fila"] = df.index
    df_list.append(df)

# Concatenar
df_master = pd.concat(df_list)

# Sacando duplicados considerando único rut
df_last = df_master.drop_duplicates(subset="RUT COMPLETO", keep='last')

# Guardar en excel, cada resultado en una pestaña
writer = pd.ExcelWriter('out/Franquicia_joined.xlsx', engine='xlsxwriter')
df_master.to_excel(writer, sheet_name="todos", index=False)
df_last.to_excel(writer, sheet_name="ultimo_segun_Rut", index=False)
writer.save()

Balance

Un 15 minutos, que garantiza que no hayan errores de copiado-pegado y selección. ¡Listo para reutilizar!

Easter Eggs | sebastiandres

Easter Eggs

Customizando fastpages.

fastpages

Para conocer un poco más del funcionamiento de fastpages, decidí personalizar un poco el sitio. Tenía ganas de agregarle algo divertido, así que me decidí por agregarle un toque absurdo a lo Monty Python.

404

La página 404 (página no encontrada) fue fácil de personalizar. Bastó editar el archivo _pages/404.html, y agregarle una nueva imagen.

Pie animado

Un desafío mayor fue agregar un pie animado que se moviera sin desplazar otros elementos. Para ello, tuve que generar un css específico:

img.monty-python-foot {
    position: absolute;
    top: -900px;
    z-index: 99;
    animation: move_down 4s;
}

@keyframes move_down {
    0% { transform: translateY(-1200px); }
    50% { transform: translateY(800px); }
    100% { transform: translateY(-1200px);
    }
}

Luego, en las páginas donde quisiera aplicarlo, bastaba con agregar una imagen con la clase respectiva:

<img src="/blog/images/monty_python_foot.png" height=800px class="monty-python-foot" />

El resultado puede verse en algunas de las páginas del sitio, es cosa de buscar.

Experiencias del Sprint de Data Umbrella | sebastiandres

Experiencias del Sprint de Data Umbrella

Lo que aprendí en mi primer sprint contribuyendo a código abierto.

codigo abierto   sklearn

Desde hace mucho tiempo tenía el deseo de contribuir “en algún momento” a un proyecto de código abierto. Sin embargo ese momento ideal siempre era pospuesto para más adelante, cuando tuviera más experiencia y más tiempo. Por eso, cuando leí sobre el Sprint de Data Umbrella en varios canales de Telegram de python y programación, no dudé y me inscribí. Era la oportunidad perfecta para obligarme a aprender. No hay nada como un poco de presión social para mover los engranajes de un procrastinador.

Para ser sincero, no ubicaba a Data Umbrella. Se trata de una organización que se preocupa de proporcionar apoyo a grupos poco representado, ya sea por género, raza, edad, orientación sexual u otros, en los campos de Machine Learning, Data Science e Inteligencia Artificial. El sprint que realizaron el 26 de Junio tenía como foco Latinoamérica, que tiene una participación baja en estos temas. El trabajo de Data Umbrella con los grupos de poca representación resulta muy valioso para derribar todos los mitos y barreras de entrada que pueden estar frenando la llegada de nuevos talentos.

Lo que más me gustó del Sprint de Data Umbrella fue la organización: tenían un checklist muy preciso de los temas a revisar, con videos explicando cada paso a paso. Por eso, era fácil estimar cuánto tiempo necesitabas preparando o aprendiendo antes del sprint. El uso de discord también ayudó mucho a darle una informalidad y aspecto comunitario, y sirvió para responder las preguntas y conocernos. Introducirse en nuevo grupo siempre es difícil, y para novatos el desafío es aún mayor. El hecho de tener un pre-sprint y post-sprint ayuda a consolidar el aspecto humano y comunitario, resolver los problemas técnicos que siempre aparecen, y ganar confianza.

Durante el sprint, el hecho de organizarnos para programar de a pares también fue de gran ayuda. Con Leonardo Rocco trabajamos en 2 issues:

¡Puedo decir con orgullo que ambos pull requests ya fueron aceptados!

Al reflexionar sobre mi experiencia en el Sprint, me doy cuenta que tenía la expectativa que me faltaban muchas cosas por aprender.

En el sprint aprendí que no se requiere ser un super-programador para contribuir al código abierto. La realidad es que, de partida, no existe una única manera de contribuir. Hay un abanico interminable de posibles trabajos, desde los más sencillos a los más avanzados, y un largo camino de aprendizaje. Por eso, es importante darse cuenta que no se trata de que “no sabes” sino que “no sabes aún, y que hay una comunidad dispuesta a apoyarte en ese proceso de aprendizaje. Todos estamos en un proceso de aprendizaje. Involucrarse en proyectos colaborativos es precisamente una manera de acelerar el aprendizaje, y de paso, contribuir a las librerías que más usas.

Hay un excelente informe del sprint en el blog de Reshama. La distribución de los participantes por países es bastante sorprendente. Imaginaba una distribución más uniforme, pero la mayoria de los participantes son de Argentina y Brasil. ¡Hay hacer algo al respecto!

Además de las contribuciones comunitarias e individuales, otro elemento importante del código abierto son las subvenciones y la financiación por parte de empresas. ¡Pídele a tu jefe presupuesto para financiar las herramientas que usas a diario! En particular, este sprint fue financiado en parte por una subvención de Code for Science & Society. Esto es comunidad y transparencia en estado puro: puedes obtener todos los detalles de la subvención en línea: Grant number GBMF8449 en Gordon and Betty Moore Foundation.

Data Umbrella Sprint - My Experience with open source | sebastiandres

Data Umbrella Sprint - My Experience with open source

Things I learned from my first open source sprint and pull request

codigo abierto   sklearn

For a long time I wanted to contribute “at some point” to an open source project. That ideal moment was always postponed until later, when I had more experience and more time. So when I was read about the Data Umbrella Sprint on several telegram channels related to python and programming, I didn’t hesitate and signed up. It was the perfect opportunity to force myself to learn. There’s nothing like a little social pressure to push the gears of a procrastinator.

To be honest, I didn’t knew Data Umbrella. It’s an organization that is concerned with providing support to underrepresented groups, whether by gender, race, age, sexual orientation or otherwise, in the fields of Machine Learning, Data Science and Artificial Intelligence. The sprint held on June 26th had a focus on Latin America, because of the low participation in these topics. Data Umbrella’s work with underrepresented groups is very valuable in breaking down all the myths and entry barriers that may be holding back new talents.

What I liked most about the Data Umbrella Sprint was the organization: they had a very precise checklist of the topics to review, with videos explaining each step by step. It was easy to estimate how much time you needed to prepare or learn before the sprint. This really helped assuring you would have all the required knowledge for the sprint. The use of discord also helped a lot to provide a community and informal aspect, and served to answer questions and get to know each other. Getting into a new group is always difficult, and for newbies the challenge may be even greater. Having a pre-sprint and post-sprint helps to consolidate the human and community aspect, solve the technical problems that always arise, and gain confidence.

During the sprint, we worked with pair-programming and proved to be of great help. With Leonardo Rocco we worked on 2 issues:

I can proudly say that both pull requests have been accepted!

Reflecting on the sprint experience, I realize that I had the expectation that I had too many things to learn before doing anything useful to open source.

In the sprint I learned that you don’t need to be a super-programmer to contribute to open source. The reality is that, to begin with, there is no single way to contribute. There is an endless range of possible jobs, from the simplest to the most advanced, and a long learning curve. Therefore, it is important to realize that it is not that “you don’t know” but that “you don’t know yet, and that there is a community willing to support you in that learning process. We are all in a learning process, from newbies to seasoned coders. Getting involved in collaborative projects is precisely a way to accelerate learning, and in the process, contribute to the libraries you use the most.

There is a great report of the sprint on Reshama’s blog. The participant’s distribution by country is quite surprising. I would have guessed a more uniform distribution, while it’s largely concentrated on Argentina and Brasil. We will have to do something about that!

Besides community and individual contributions, another big piece on open code are grants and funding from companies. Go ask your manager for budget to financing the tools you use day-to-day! In particular, this sprint was funded in part by a grant from Code for Science & Society. Community and transparency at its best: you can get all the details for the grant online: Grant number GBMF8449 at Gordon and Betty Moore Foundation.

Fastpages 2: reloaded | sebastiandres

Fastpages 2: reloaded

Las grandes ideas nunca mueren.

fastpages

Ya perdí la cuenta de la cantidad de veces que he armado un sitio web para publicar posts y jupyter notebook. Esta vez espero ser más constante. Estuve averiguando sobre como funciona, y acá aprovecho de poner mis notas.

¿Cómo funciona fastpages?

Al crear un blog con fastpages se generan 2 ramas:

  • En la rama master se encuentran los posts en formato markdown, jupyter notebooks o word, los archivos de configuración, y distintos archivo de html, css y javascript.
  • En la rama gh-pages se encuentra únicamente el html que se genera a partir del contenido de la rama master, y que se despliega como una página web en <username>.github.io/<repository-name> gracias a Github Pages (sino, se vería como el clásico repositorio de código).

Cuando haces push de un cambio a master, se ejecutan automáticamente 3 acciones mediante github actions:

  1. check_config.yaml: Revisa que los archivos de configuración sean consistentes y tengan la información necesaria. Esto toma usualmente unos 15 segundos.
  2. ci.yaml: Toma unos 90 segundos en completar los siguientes pasos.
    • Crea un ambiente linux donde se realizarán todas las operaciones.
    • Chequea que tengas permiso hayas definido SSH_DEPLOY_KEYS
    • Descarga la ultima versión del repositorio (la versión que recien pusheaste) de la rama master.
    • Convierte a markdown todos los archivos de jupyter notebook y word.
    • Descarga el tema de Jekyll minima para tener todos los html, css y javascript necesarios.
    • Usa Jekyll para convertir todos los markdowns a páginas web con el tema minima.
    • Copia el html generado a la rama gh-pages.
  3. gh-pages.yaml: Revisa que todo se haya generado correctamente (creo).

Personalizando la página principal

Para personalizar la página principal, basta con modificar ./index.html. La página principal que se verá en el sitio web se genera de manera encadenada. En index.html se define que se usará el layout home.html, que a su vez define que usará el layout default.html. Es posible modificar ./layouts/home.html tomará el texto de ./index.html y lo convertirá de markdown a html, y le agregará un contenido adicional.

Personalizando el favicon

El favicon se puede modificar reemplazando directamente images/favicon.ico, o bien, editando el archivo _includes/favicons.html para definir una ruta diferente.

Es posible generar un favicon de manera gratuita en realfavicongenerator.

Agregar nuevas páginas

Para agregar nuevas páginas, basta con agregar los archivos en la carpeta _pages. Los archivos agregados pueden ser html o markdown, pero tienen la misma estructura:

---
layout: default
permalink: /desired-path/
title: Desired Name
search_exclude: true
---

SOME TEXT IN HTML OR MARKDOWN

El layout debe ser default o uno de los layouts definidos en la carpeta _layouts. Se puede usar html o markdown, según lo que sea más conveniente usar.

Microcuentos tecnología | sebastiandres

Microcuentos tecnología

Cuentos enviados a concurso uPlanner en 100 palabras

escritura

Todo cobra sentido

Esperando el metro, escuchó una conversación entre dos estudiantes. ¿Porqué elegiste esa U? - preguntó uno. Y el otro respondió: ¿No cachai? Usan uPlanner.

Nica

Tu perfil y antecedentes laborales son impecables, serías un buen fit para Customer Success. Le di las gracias a la entrevistadora, y pasé a despedirme de Nicolás Diaz, que me había reenviado la oferta laboral. Me despedí también de Nicolás Walters y Nicolás Mena, esperando haber causado una buena impresión. Al día siguiente me llamaron. Lo siento -me dijo Nicole Lorenzini- pero ya tenemos un Nicolás González, y sería demasiado confuso para todos.

Siglas

El PM le comentó al PO que el reporte PBI de NPS estaba fallando por un problema del ETL, cuando el SQL no conectaba a la VPN. El PO había estado en JIRA, loggeando el MVP de GDPR para SaaS. El PM le pidió al PO no olvidar su OKR para aumentar el ARR mediante un mejor SEO para SMB. El CEO les recordó el valor de la cooperación, o los mandaría a la CTM mediante una expedita PLR, ¿OK?

Emulador de Linktree | sebastiandres

Emulador de Linktree

Imitando un directorio de enlaces sólo con recursos gratuitos

fastpages   markdown

Un directorio de enlaces permite tener entregar un link único y memorable, que puedes actualizar posteriormente a haberlo compartido. Donde, por ejemplo, al dar una charla pudiera decir “el código y la presentación estarán en este link”, pero donde posteriormente sea posible agregarle la grabación de la charla o un resultados de una encuesta. Probé con Linktree, pero pronto lo llené con demasiados enlaces y se volvió confuso y difícil de navegar. Necesitaba una forma de agrupar de manera temática y con un grado flexible de niveles de profundad. En efecto, si a alguien no le interesa el tema “X” probablemente no le interesaría el video de “X”, el código de “X”, la presentación de “X” en html o pdf.

Formato ofrecido por linktree (izquieda) versus lo que necesitaba y que tuve que crear (derecha). Necesitaba una manera de tener un directorio de enlaces con un nombre memorable, fácil de compartir y personalizar, pero permitiendo mantener distintos niveles de agrupación. Desafío adicional: Cuando se trata de recursos en línea, soy un tacaño extremo. Así que buscaba una solución que pudiera implementar sin costos mensuales recurrentes.

Como tacaño extremo llegué a pensar en hacer varias cuentas en linktree y vincularlas entre sí, pero no era una solución escalable (y tendría que acordarme de que correo y password usé para cada enlace). Así que entre una mezcla de vergüenza y flojera terminé desechando la idea. La solución me llegó de golpe. Github permite mostrar páginas html estáticas. ¡Podía hacer una página html simple que emulara a linktree y que podía guardar en cada repositorio en github! El enlace central seguiría siendo en linktree (lintr.ee/sebastiandres en mi caso) y los enlaces serían a los htmls plano de cada repositorio de github. Es una buena mezcla, porque linktree es bastante amigable y tiene algunas herramientas de administración como activar/desactivar enlaces. Comencé revisando el código de linktree, pero era excesivamente largo y confuso. Así que puse manos a la obra y me puse a construir una solución de manera incremental.

  • Primera versión: Un único archivo html, simple y funcional. Imita los colores y botones sólo con html y css, sin javascript, incluyendo la animación del botón. Comienzo a desplegarlo en mis repositorios y enlazarlos desde linktree. Basta con activar cada repositorio como github pages para que github proporcione un enlace fijo y estable para index.html (o README.md). No es perfecto, pero funciona, ya se cumple el funcionamiento mínimo. Problemas detectados: el tipo y tamaño de fuente es similar pero no es correcto. El despliegue en un smartphone es muy distinto entre linktree y imitación (tacaña).

  • Segunda versión: Después de googlear, aprendí a manejar css selectivo usando @media (orientation: landscape/portrait). Problemas detectados: Todavía se ve un poco diferente, sobre todo en los márgenes y tamaño del botón. Al ir actualizando en mis repositorios esta segunda versión, me doy cuenta que es difícil recordar y distinguir visualmente cuál es la versión actual. Sería interesante tener algún tipo de indicador visual al respecto. Tercera versión: Ajustes a las propiedades del css para imitar mejor linktree (width, height, text wrapping, font sizes). Incluye un texto con la versión para distinguir la versión utilizada en cada repositorio y hacer más fácil la actualización. Problemas detectados: Los textos no están centrados adecuadamente. Lo peor es tener que ir copiando manualmente el nuevo estilo a cada repositorio en cada nueva actualización. Lo ideal es que el estilo se manejara centralmente y actualizara automáticamente a todos los repositorios que lo utilizaran.

  • Cuarta versión: En lugar de tener el css directamente en el html, lo muevo a un archivo css. De esa manera, todos los archivos pueden llamar a ese archivo. Eso permite separar contenido de formato. Esta cuarta versión me dio muchos problemas. En local todo funcionaba bien, pero github no desplegaba bien la página por un problema con el MIME type del archivo css. Así que tuve que usar https://raw.githack.com para obtener un enlace del css con el MIME type correcto. Problemas detectados: La actualización de css con github a través de https://raw.githack.com tiene un lag importante, a veces de horas, por lo que es difícil (y frustrante) comprobar si el cambio resultó bien.

  • Quinta versión: Comienzo a utilizar DigitalOcean para levantar un sitio estático a partir del repositorio en github. De esa manera, se generan urls fijas de los assets (css o eventualmente javascript) que se actualizan automáticamente con cada push a github, sin costo asociado y con el MIME type adecuado. Eso permite tener un enlace estable para css y html que puede utilizarse en los repos de github. Ahora también externalizo la “versión” del archivo, para que sea importado por el archivo. Estos archivos css y html ahora se llamarán siempre “latest_style.css” y “latest_copyright.html”, y que actualizo con cada cambio de versión. Así el archivo final se actualiza automáticamente cuando hay algún cambio (¡¡¡no más cambios masivos en los repos!!!). Se definen también algunas clases adicionales para manejar títulos y subtítulos. Problemas detectados: En general se observa bien, pero hay algunos glitchs de tamaño de fuente, textos centrados y detalles así.

* Sexta (y última) versión: Revisión general del código para optimizarlo y limpiarlo. Se eliminan algunas propiedades css innecesarias. Hay 3 archivos: El archivo principal es index.html donde se definen los enlaces, y se utilizan los archivos latest_style.css (estilo) y latest_copyright.html (versión de emulador). Cada nueva página con enlaces sólo necesita definir los enlaces, ¡el estilo y copyright ya está definido! Problemas detectados: Nada por ahora.

Sneak peak:

¿Cómo es el código? La primera versión mezcla el código y el estilo. Hace que actualizar múltiples archivos sea tedioso y propenso a errores (pero fue una versión rápida de desarrollar y autocontenida).

<!DOCTYPE html>
<html>
    <head>
        <style>
            html {background-color:#3d3b3c; font-family: Karla, sans-serif; 
                  font-size: 16px; font-weight: 700;}
            h3, h2, h1 { color:#ffffff; margin-bottom: 16px; text-align: center;}
            div {position: fixed; top: 25%; left: 50%; 
                -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%);}
            a {text-decoration: none!important;}
            p  {background-color:#ffffff; color:#3d3b3c; line-height: 56px; width: 676px;
                border: 2px solid rgb(255, 255, 255); margin-bottom: 16px; text-align: center;}
            p:hover {background-color:#3d3b3c; color:rgb(255, 255, 255);}
        </style>
    </head>

    <body>
        <div>
            <h1>Emulador de Linktree</h1>
            <h3>Enlaces</h3>
            <a href="https://linktree-ixkge.ondigitalocean.app/demo1.html" target="_blank">
                <p>Primera versión</p>
            </a>
            <a href="https://linktree-ixkge.ondigitalocean.app/demo6.html" target="_blank">
                <p>Última versión</p>
            </a>
            <h3>Enlaces</h3>
            <a href="https://linktr.ee/sebastiandres">
                <p>⇦ linktree</p>
            </a>
        </div>
    </body>
</html>

La última versión permite cambiar el estilo y copyright de manera centralizada, y preocuparse únicamente del contenido.

<!DOCTYPE html>
<html>
    <head>
        <link href="https://fonts.googleapis.com/css2?family=Karla:wght@300;400;600;700&amp;display=swap" rel="stylesheet">
        <link href="https://linktree-ixkge.ondigitalocean.app/latest_style.css" rel="stylesheet"> 
    </head>
    <body>
        <div>
            <h1>Emulador de Linktree</h1>
            <h3>Enlaces</h3>
            <a href="https://linktree-ixkge.ondigitalocean.app/demo1.html" target="_blank">
                <p>Primera versión</p>
            </a>
            <a href="https://linktree-ixkge.ondigitalocean.app/demo6.html" target="_blank">
                <p>Última versión</p>
            </a>
            <h3>Enlaces</h3>
            <a href="https://linktr.ee/sebastiandres">
                <p>⇦ linktree</p>
            </a>
        </div>
        <!-- Version - dont change this code-->       
        <object data="https://linktree-ixkge.ondigitalocean.app/latest_copyright.html" width=100%></object>
    </body>
</html>

¿Y cómo se ve todo al final? Se ve así:

Aprendizajes:

La separación de contenido y forma es esencial para poder ahorrar trabajo. Incluso para un proyecto pequeño como éste, ayuda enormemente a no duplicar trabajo y hacer más fácil las actualizaciones de N páginas.

La primera versión, que tenía el 80% de las funcionalidades me tomó el 20% del tiempo invertido. Pero el 80% del tiempo restante me enseñó muchas cosas que no sabía de html y css, mime types, y fue muy valioso.

Crear Github pages por cada repositorio es muy práctico, pero tiene ciertas limitaciones para que la gente no abuse al crear páginas estáticas y sea utilizado como un webserver gratis. Existen algunos sitios para entregar css y javascript con el MIME type correcto (https://raw.githack.com por ejemplo), pero no se actualizan automáticamente. DigitalOcean permite levantar sitios estáticos a costo cero y con actualización automática, y poder tener todo sincronizado.

Enlaces de interés:

  • https://linktr.ee/sebastiandres: Mi directorio de enlaces, el sitio que quería imitar. linktree-ixkge.ondigitalocean.app: Sitio web estático, sirve como demo de “cómo se vería” y para almacenar/enlazar el css y copyright.
  • https://github.com/sebastiandres/linktree: Repositorio con las distintas versiones del código. Puedes tomar la última versión de html, css y copyright y personalizarlo a tu antojo.
  • ¿Cosas por mejorar? ¡Muchas! ¡Sugerencias y comentarios bienvenidos!
Microcuentos 2021 | sebastiandres

Microcuentos 2021

Cuentos enviados a Santiago en 100 palabras

escritura

Canciones pendientes

Es una situación extraña, no me pasa con la pintura, escritura, cine u otras artes. Sin embargo, cuando pienso en la cantidad de canciones que no llegaron a escribirse, me da una pena paralizadora. Estoy completamente seguro que John Lennon nos habría regalado un nuevo Yesterday que nunca llegaremos a escuchar. O que las manos de Victor Jara habrían rasgado de su guitarra otro canto poético y atemporal. Me persigue la imagen de sus muertes sin sentido. ¿Se puede tener nostalgia de una canción que nunca has escuchado?

Cada santo con su milagro

Mi abuela peregrinaba a Lo Vásquez. Mi madre le rezaba a San Expedito. Yo soy apóstol de San Bielsa y le prendo velitas al Omeprazol.

Rudy de Loncoche

Cuando cuando viajábamos con mi abuelo, cada lugar tenía una historia: acá vivía Fulano, acá trabajaba Mengano, ahí se mató Zutano. Muchas veces pensé en sentarme con él, escuchar y anotar cada una de sus historias. Pero me daba susto. Pensaba que esas anécdotas eran como el pelo de Sansón o el talón de Aquiles. Al escribirlas, él perdería esa magia inexplicable que lo había protegido de accidentes y operaciones. Tantos viajes al médico que ya había perdido la cuenta. Al final, la muerte se lo llevó igual y sólo me quedaron algunas de sus más famosas historias.

Gol o penal

Dos mochilas marcan el arco de un equipo. Los sin polera delimitan el suyo con 2 montones de ropa. Nadie mira la hora. La calle tiene sus reglas. El partido sólo termina cuando el perdedor concede la derrota.

Gustavo Adolfo se revuelve en su tumba

¿Qué es poesía?, dices mientras clavas en mi pupila tu pupila azul. Y tras consultar la wikipedia, te respondo. Poesía es un género literario considerado como una manifestación de la belleza o del sentimiento estético por medio de la palabra, en verso o en prosa. Dices que mi modernidad mata el romanticismo. No te compartiré mi excel con la tasa de divorcios, ni mi listado de pros y contras del matrimonio. La decisión fue fácil de evaluar pero difícil de tomar. Una lágrima que no puedo contener, de 0.25 ml, corre lenta por mi mejilla mientras te alejas para siempre.

Santiago en 150 palabras

Sentado-al-computador,-el-escritor-descubrió-que-podía-escribir-cuentos-para-Santiago-en-más-de-cien-palabras. Simplemente/tenía/que/reemplazar/los/espacios/por/otro/símbolo,/y/eso/confundía/al/sistema/de/postulación. No&gritó&eureka&ni&salió&corriendo&desnudo&de&la&bañera. Pensó_que_gracias_al_descubrimiento_todo_sería_más_fácil_al_no_tener_límites_para_su_escritura. Sin^embargo,^y^tras^varios^días^sentado^frente^al^computador,^la^página^seguía^en^blanco^y^su^historia^seguía^sin^escribirse. Llevabatantotiemposoloyencerradoquecarecíadelingredienteprincipal:recuerdosconloscualestejeruna*historia. Todo+lo+recordaba+lejano+y+difuso,+como+una+foto+antigua+cubierta+con+una+fina+capa+de+polvo+gris. Ya·no·recordaba·la·risa·de·los·niños·en·el·parque,·las·bromas·de·una·noche·de·tragos·con·los·amigos,·la·pasión·de·un·romance,·o·un·simple·paseo·sin·rumbo·bajo·los·árboles. Y sin historia que contar, su descubrimiento nunca llegó a ser conocido.

Examen

Sin poderlo evitar, miro de nuevo por la ventana de la biblioteca. Afuera llueve. El mar azota la costa. Adentro, apilo libros de métodos estocásticos sobre mi escritorio. Y aunque todo invita a la emoción y trato de descansar, no puedo evitar pensar ¿caerán las gotas probabilísticamente determinadas?

IPython Display - componentes web | sebastiandres

IPython Display - componentes web

Componentes web para agregar interactividad a Jupyter Notebook/Lab.

jupyter   rise

Contenidos

IPython.Display - la parte web

En IPython.display existen 23 clases que son muy prácticas para agregar interactividad a Jupyter Notebook/Lab.

Acá probamos únicamente 3 que se relacionan con la web: IFrame, HTML, Javascript.

1. Antes de: principales errores

1.1 Usando display generosamente

Mi principal aprendizaje es que IPython.display genera objetos de distintos tipos. Los objetos no se muestran o ejecutan por defecto. Si el objeto es el resultado de la celda, el objeto si se despliega o ejecuta.

Si no es el resultado de la celda, no se muestra/ejecuta, a menos que se use el método display.

Diferencias entre hermanos

Jupyter Notebook, Jupyter Lab y Google Colab no son completamente similares. Hasta ahora, lo que he visto:

  • En Google Colab está IPython 5.0.0. No es lo mejor para evaluar javascript. De hecho, hay funciones que no existen: alert y prompt no están.

Link interesante: https://colab.research.google.com/notebooks/snippets/advanced_outputs.ipynb#scrollTo=iXZ0xoQd2kCe

2. Páginas web

2.1 IFrame

IFrame(src, 
       width, 
       height, 
       **kwargs)

Permite insertar una página web como un IFrame (inline frame). La página debe permitirlo (no todas lo hacen). Por ejemplo, para insertar una encuesta o una referencia en un jupyter notebook o una presentación con RISE.

Métodos o atributos del objeto creado:

  • iframe: String con la representación HTML del objeto. No es muy útil.
from IPython.display import IFrame
IFrame("https://es.wikipedia.org/wiki/IFrame", width=800, height=400)

Para más información, ver la documentación con IFrame.?

2.2 Javascript

Javascript(data=None, 
           url=None, 
           filename=None, 
           lib=None, 
           css=None)

Carga el código javascript en la página. En el notebook, el elemento conteneder será element, y jQuery estará disponible. Contenido agregado a element serán visibles en la área de output.

Métodos o atributos del objeto creado:

  • metadata: Atributo string con la metadata con la que fue creado el objeto.
  • reaload: Método para recargar el contenido del objeto, si fue creado desde url o archivo.
from IPython.display import Javascript
Javascript('alert("¡¡Tenemos javascript!!");')
from IPython.display import Javascript
Javascript('console.log("¡¡Tenemos javascript!!");')
from IPython.display import Javascript
js_str = """
var a = 1;
console.log("JS1: a = "+a);
var b = 2;
console.log("JS1: b = " + b);
console.log("JS1: a+b = " + (a+b));
"""
Javascript(js_str)
from IPython.display import Javascript, display
js_str_1 = """
var a = 1;
console.log("JS2: a ="+a);
"""
js_str_2 = """
var b = 2;
console.log("JS2: b =" + b);
console.log("JS2: a+b=" + (a+b));
"""
js_1 = Javascript(js_str_1)
js_2 = Javascript(js_str_2)
display(js_1, js_2)
from IPython.display import Javascript
Javascript?
Init signature: Javascript(data=None, url=None, filename=None, lib=None, css=None)
Docstring:      Validate that display data is text
Init docstring:
Create a Javascript display object given raw data.

When this object is returned by an expression or passed to the
display function, it will result in the data being displayed
in the frontend. If the data is a URL, the data will first be
downloaded and then displayed.

In the Notebook, the containing element will be available as `element`,
and jQuery will be available.  Content appended to `element` will be
visible in the output area.

Parameters
----------
data : unicode, str or bytes
    The Javascript source code or a URL to download it from.
url : unicode
    A URL to download the data from.
filename : unicode
    Path to a local file to load the data from.
lib : list or str
    A sequence of Javascript library URLs to load asynchronously before
    running the source code. The full URLs of the libraries should
    be given. A single Javascript library URL can also be given as a
    string.
css: : list or str
    A sequence of css files to load before running the source code.
    The full URLs of the css files should be given. A single css URL
    can also be given as a string.
File:           /miniconda3/lib/python3.7/site-packages/IPython/core/display.py
Type:           type
Subclasses:     
from IPython.display import Javascript
print("Methods:")
print(sorted([f for f in dir(Javascript) if not f.startswith('_')]))

2.3 HTML

HTML(data=None, 
     url=None, 
     filename=None, 
     metadata=None)

Observación/Consejos:

  • No incluir secciones de <html>, <head> o <body>.
  • El Javascript vive en la celda de notebook, las variables no se traspasan de una celda a otra. Ver los ejemplos al final.
  • En Google Colab algunas funciones de javascript no están disponibles, en particular 'alert'.

Métodos o atributos del objeto creado:

  • metadata: String con la metadata con la que fue creado el objeto.
  • reaload(): Método para recargar el contenido del objeto, si fue creado desde url o archivo.
from IPython.display import HTML
HTML("<div style='background-color:lightblue'>Este es un ejemplo.</div>")
Este es un ejemplo.
# Ver resultado con la consola de javascript
from IPython.display import HTML
my_html = """
<script>
function button_action(value){
  var msg = 'La variable tiene valor '+ value;
  console.log(msg);
  alert(msg);
};
</script>

<div> 
Variable: 
<input type='text' id='my_input_form' value='42'> 
<button onclick='button_action(document.getElementById("my_input_form").value)'>Click</button>
</div>
"""
HTML(data=my_html)
Variable:
from IPython.display import HTML
my_filename = "html/ejemplo.html"
HTML(filename=my_filename)

Título Página HTM

from IPython.display import HTML
my_url = "https://raw.githubusercontent.com/sebastiandres/2021-05-IPython-display/master/html/ejemplo.html"
HTML(url=my_url)

Título Página HTM

from IPython.display import HTML
my_html = """
<script>
var a = 1;
console.log("HTML: a ="+a);
</script>
"""
HTML(my_html)

Más información

from IPython.display import HTML
HTML?
from IPython.display import HTML
print("Methods:")
print(sorted([f for f in dir(HTML) if not f.startswith('_')]))

3. Experimentos

3.1 Experimento HTML 1:

Valores de variables no se traspasan de una celda a otra. Definiremos aen la primera celda, y trataremos de imprimir el valor de a en la celda siguiente.

from IPython.display import HTML
my_html = """
<script>
var num = 42;
console.log("HTML1A: num = " + num);
</script>
"""
HTML(my_html)
from IPython.display import Javascript
my_html = """
<script>
var b = 2;
console.log("HTML1B1: num =" + num);
console.log("HTML1B2: b =" + b);
console.log("HTML1B3: num+b=" + (num+b));
</script>
"""
HTML(my_html)

Experimento HTML 2:

Colocando varios scripts en un único html: si funciona.

from IPython.display import HTML
my_html = """
<script>
var a = 1;
console.log("HTML2A: a = "+a);
</script>

<script>
var b = 2;
console.log("HTML2B: b = "+b);
</script>

<script>
console.log("HTML2C: a+b = "+(a+b));
</script>
"""
HTML(my_html)

Experimento HTML 3:

Ejecutando varios HTMLs por separado.

from IPython.display import HTML, display
my_html = """
<script>
var a = 1;
console.log("HTM3: a = "+a);
</script>
"""
h1 = HTML(my_html)
my_html = """
<script>
var b = 2;
console.log("HTML3: b = "+b);
</script>
"""
h2 = HTML(my_html)
my_html = """
<script>
console.log("HTML3: a+b = "+(a+b));
</script>
"""
h3 = HTML(my_html)
display(h1, h2, h3) # Hay que poner todos, sino solo muestra el último

Experimento HTML 4:

El hermano pobre RISE, ¿cómo hacer presentaciones con reveal.js?

!cat body.html
<body>

<link rel="stylesheet" href="reveal.css">
<script src="reveal.js"></script>
<script src="body.js"></script>

<button id="my_button" onclick="launch_reveal()">Click me</button>

<div id="reveal_id" class="reveal">
	<div class="slides">
		<section>Slide 1</section>
		<section>Slide 2</section>
		<section>Slide 3</section>
	</div>
</div>

</body>
from IPython.display import HTML, Javascript, display
HTML(filename="body.html")
Slide 1
Slide 2
Slide 3
from IPython.display import HTML, Javascript, display
my_path = "https://raw.githubusercontent.com/sebastiandres/2021-05-IPython-display/master/"
my_js = Javascript(url=my_path+"body.js", lib=my_path+"reveal.js", css=my_path+"reveal.css")
my_html = HTML(filename="body.html")
display(my_js)
display(my_html)
Slide 1
Slide 2
Slide 3
from IPython.display import HTML, Javascript, display
my_js = """
var link = document.createElement('link');
link.rel = "stylesheet"; link.type = "text/css";
link.href = "reveal.css";
document.querySelector('head').appendChild(link);
"""
Javascript(my_js)

Experimento JS 1:

Ejecutando varios javascript por separado.

from IPython.display import Javascript
js_str_1 = """
var num = 42;
console.log("JS1A: num = " + num);
"""
Javascript(js_str_1)
from IPython.display import Javascript
js_str_2 = """
var num = 42;
var b = 2;
console.log("JS1B1: num = " + num);
console.log("JS1B2: b = " + b);
console.log("JS1B3: num+b = " + (num+b));
"""
Javascript(js_str_2)

Experimento JS 2:

Usando javascript (puro) para agregar elementos html de manera dinámica. Observación: No se usa html, sino sólo Javascript sobre el "element".

from IPython.display import Javascript
my_js = """
function button_action(value){
  console.log('La variable tiene valor '+ value);
};

element.append(`<div> 
Variable: 
<input type='text' id='my_input_form' value='42'> 
<button onclick='button_action(document.getElementById("my_input_form").value)'>Click</button>
</div>`);
"""
Javascript(my_js)

Experimento JS 3:

Usando javascript para graficar.

CURRENTLY NOT WORKING

# https://github.com/timqian/chart.xkcd
from IPython.display import Javascript
my_js = """
  const svg = document.querySelector('.line-chart')

  new chartXkcd.Line(svg, {
    title: 'Monthly income of an indie developer',
    xLabel: 'Month',
    yLabel: '$ Dollars',
    data: {
      labels:['1', '2', '3', '4', '5', '6','7', '8', '9', '10'],
      datasets: [{
        label: 'Plan',
        data: [30, 70, 200, 300, 500 ,800, 1500, 2900, 5000, 8000],
      }, {
        label: 'Reality',
        data: [0, 1, 30, 70, 80, 100, 50, 80, 40, 150],
      }]
    },
    options: {}
  });
  
  element.append(`<svg class="line-chart"></svg>`)
"""
my_lib = "https://cdn.jsdelivr.net/npm/chart.xkcd@1/dist/chart.xkcd.min.js"
Javascript(data=my_js, lib=my_lib)

Experimento JS 3:

Usando javascript para graficar.

CURRENTLY NOT WORKING

from IPython.display import Javascript
# Link: https://github.com/timqian/chart.xkcd/blob/master/examples/index.js
my_url = "https://raw.githubusercontent.com/timqian/chart.xkcd/master/examples/index.js"
my_lib = "https://cdn.jsdelivr.net/npm/chart.xkcd@1/dist/chart.xkcd.min.js"
Javascript(url=my_url, lib=my_lib)
import IPython

js_code = \
'''
let message = "Hello world!";
//document.querySelector("#output-area").appendChild(document.createTextNode(message));
element.append(document.createTextNode(message));
'''

display(IPython.display.Javascript(js_code))
import IPython

js_code = \
'''
var a = prompt("Hello", "Sebas");
alert(a);
'''

display(IPython.display.Javascript(js_code))
from IPython.display import HTML, Image

canvas_html = """
<canvas width=%d height=%d style="background-color:rgb(240,240,240)"></canvas>
<button>Guess Number</button>
<script>
var canvas = document.querySelector('canvas')
var ctx = canvas.getContext('2d')
ctx.lineWidth = %d
var button = document.querySelector('button')
var mouse = {x: 0, y: 0}
canvas.addEventListener('mousemove', function(e) {
  mouse.x = e.pageX - this.offsetLeft
  mouse.y = e.pageY - this.offsetTop
})
canvas.onmousedown = ()=>{
  ctx.beginPath()
  ctx.moveTo(mouse.x, mouse.y)
  canvas.addEventListener('mousemove', onPaint)
}
canvas.onmouseup = ()=>{
  canvas.removeEventListener('mousemove', onPaint)
}
var onPaint = ()=>{
  ctx.lineTo(mouse.x, mouse.y)
  ctx.stroke()
}
var data = new Promise(resolve=>{
  button.onclick = ()=>{
    resolve(canvas.toDataURL('image/png'))
  }
})
</script>
"""
print(canvas_html % (280, 280, 10))

def draw(filename='drawing.png', w=280, h=280, line_width=10):
  display(HTML(canvas_html % (w, h, line_width)))

draw()
<canvas width=280 height=280 style="background-color:rgb(240,240,240)"></canvas>
<button>Guess Number</button>
<script>
var canvas = document.querySelector('canvas')
var ctx = canvas.getContext('2d')
ctx.lineWidth = 10
var button = document.querySelector('button')
var mouse = {x: 0, y: 0}
canvas.addEventListener('mousemove', function(e) {
  mouse.x = e.pageX - this.offsetLeft
  mouse.y = e.pageY - this.offsetTop
})
canvas.onmousedown = ()=>{
  ctx.beginPath()
  ctx.moveTo(mouse.x, mouse.y)
  canvas.addEventListener('mousemove', onPaint)
}
canvas.onmouseup = ()=>{
  canvas.removeEventListener('mousemove', onPaint)
}
var onPaint = ()=>{
  ctx.lineTo(mouse.x, mouse.y)
  ctx.stroke()
}
var data = new Promise(resolve=>{
  button.onclick = ()=>{
    resolve(canvas.toDataURL('image/png'))
  }
})
</script>

from IPython.display import HTML
HTML(filename="drawing.html")

IPython Display - componentes no web | sebastiandres

IPython Display - componentes no web

Componentes no web para agregar interactividad a Jupyter Notebook/Lab.

jupyter   rise

Contenidos

Probaremos los siguientes:

  • Imágenes
    • SVG
    • Image
  • Video
    • Video
    • VimeoVideo
    • YoutubeVideo
  • Audio
    • Audio
  • Texto o Código
    • Code
    • FileLink y FileLinks
    • Javascript
    • Latex
    • Markdown
    • Math
    • Scribd Document
  • Otros
    • JSON
    • GEOJSon
    • TextDisplayObject
    • DisplayObject
    • DisplayHandle
    • Pretty

1. Antes de: principales errores

Mi principal aprendizaje es que lo que se genera son objetos de distintos tipos. Los objetos no se muestran por defecto. Si el objeto es el resultado de la celda, el objeto se despliega por defecto. Sino es el resultado de la celda, no se muestra.

Es posible mostrar uno o varios objetos, con la función display. Display funciona en jupyter notebook/lab, pero no en python o IPython.

from IPython.display import Audio
Audio("http://www.w3schools.com/html/horse.ogg")
from IPython.display import Audio
a = Audio("http://www.w3schools.com/html/horse.ogg")
from IPython.display import Audio, display
a = Audio("http://www.w3schools.com/html/horse.ogg")
display(a)
from IPython.display import Audio, Image, display
a = Audio("http://www.w3schools.com/html/horse.ogg")
i1 = Image(filename="2021-05-ipython-display/Python.png", width=100)
i2 = Image(filename="2021-05-ipython-display/Python.jpg", width=150)
display(a,i1,i2)

2. Diferencias Jupyter Notebook/Lab y Google Colab

En Jupyter Notebook puedes instalar cualquier versión de IPython. Acá probamos la versión 7.7; en Google Colab está instalado IPython 5.5.5, por lo que no funcionará todo.

 3 Imágenes

3.1 Image

Image(data=None,
      url=None,
      filename=None,
      format=None,
      embed=None,
      width=None,
      height=None,
      retina=False,
      unconfined=False,
      metadata=None)

Métodos o atributos del objeto creado:

  • metadata: Atributo string con la metadata con la que fue creado el objeto.
  • reaload: Método para recargar el contenido del objeto, si fue creado desde url o archivo.
from IPython.display import Image
Image(filename="2021-05-ipython-display/Python.png", width=100)
from IPython.display import Image
Image(filename="2021-05-ipython-display/Python.jpg", width=150)
from IPython.display import Image
Image(filename="2021-05-ipython-display/Python.gif", width=100)
<IPython.core.display.Image object>
from IPython.display import Image
my_url = "https://raw.githubusercontent.com/sebastiandres/2021-05-IPython-display/master/images/Python.png"
Image(url=my_url, width=100)
None
from IPython.display import Image
import numpy as np
my_data = ""
a = Image(data=my_data)

Más información

from IPython.display import Image
Image?

2.18 SVG

class IPython.display.SVG(data=None,
                          url=None,
                          filename=None, 
                          metadata=None)

Un objeto SVG creado tiene los siguientes métodos o atributos:

  • data: Entrega los datos del SVG.
  • metadata: String con la metadata proporcionada en la creación.
  • reload: Método para recargar el SVG, si fue creado por url o filename.
from IPython.display import SVG
SVG(filename="2021-05-ipython-display/Python.svg")
image/svg+xml
from IPython.display import SVG
SVG(url="https://raw.githubusercontent.com/sebastiandres/2021-05-IPython-display/master/images/Python.svg")
image/svg+xml
from IPython.display import SVG, display
data = '<svg height="109.8461" id="svg2169" inkscape:output_extension="org.inkscape.output.svg.inkscape" inkscape:version="0.45.1" sodipodi:docbase="/home/bene/Desktop" sodipodi:docname="dessin-1.svg" sodipodi:version="0.32" version="1.0" width="110.4211" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">\n  <defs id="defs2171">\n    <linearGradient id="linearGradient11301" inkscape:collect="always">\n      <stop id="stop11303" offset="0" style="stop-color:#ffe052;stop-opacity:1"/>\n      <stop id="stop11305" offset="1" style="stop-color:#ffc331;stop-opacity:1"/>\n    </linearGradient>\n    <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient11307" inkscape:collect="always" x1="89.136749" x2="147.77737" xlink:href="#linearGradient11301" y1="111.92053" y2="168.1012"/>\n    <linearGradient id="linearGradient9515" inkscape:collect="always">\n      <stop id="stop9517" offset="0" style="stop-color:#387eb8;stop-opacity:1"/>\n      <stop id="stop9519" offset="1" style="stop-color:#366994;stop-opacity:1"/>\n    </linearGradient>\n    <linearGradient gradientUnits="userSpaceOnUse" id="linearGradient9521" inkscape:collect="always" x1="55.549179" x2="110.14919" xlink:href="#linearGradient9515" y1="77.070274" y2="131.85291"/>\n  </defs>\n  <sodipodi:namedview bordercolor="#666666" borderopacity="1.0" height="184.25197px" id="base" inkscape:current-layer="layer1" inkscape:cx="-260.46312" inkscape:cy="316.02744" inkscape:document-units="px" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="624" inkscape:window-width="872" inkscape:window-x="5" inkscape:window-y="48" inkscape:zoom="0.24748737" pagecolor="#ffffff" width="131.10236px"/>\n  <metadata id="metadata2174">\n    <rdf:RDF>\n      <cc:Work rdf:about="">\n        <dc:format>image/svg+xml</dc:format>\n        <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>\n      </cc:Work>\n    </rdf:RDF>\n  </metadata>\n  <g id="layer1" inkscape:groupmode="layer" inkscape:label="Calque 1" transform="translate(-473.36088,-251.72485)">\n    <g id="g1894" transform="translate(428.42338,184.2561)">\n      <path d="M 99.75,67.46875 C 71.718268,67.468752 73.46875,79.625 73.46875,79.625 L 73.5,92.21875 L 100.25,92.21875 L 100.25,96 L 62.875,96 C 62.875,96 44.9375,93.965724 44.9375,122.25 C 44.937498,150.53427 60.59375,149.53125 60.59375,149.53125 L 69.9375,149.53125 L 69.9375,136.40625 C 69.9375,136.40625 69.433848,120.75 85.34375,120.75 C 101.25365,120.75 111.875,120.75 111.875,120.75 C 111.875,120.75 126.78125,120.99096 126.78125,106.34375 C 126.78125,91.696544 126.78125,82.125 126.78125,82.125 C 126.78125,82.124998 129.04443,67.46875 99.75,67.46875 z M 85,75.9375 C 87.661429,75.937498 89.8125,78.088571 89.8125,80.75 C 89.812502,83.411429 87.661429,85.5625 85,85.5625 C 82.338571,85.562502 80.1875,83.411429 80.1875,80.75 C 80.187498,78.088571 82.338571,75.9375 85,75.9375 z " id="path8615" style="opacity:1;color:#000000;fill:url(#linearGradient9521);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"/>\n      <path d="M 100.5461,177.31485 C 128.57784,177.31485 126.82735,165.1586 126.82735,165.1586 L 126.7961,152.56485 L 100.0461,152.56485 L 100.0461,148.7836 L 137.4211,148.7836 C 137.4211,148.7836 155.3586,150.81787 155.3586,122.53359 C 155.35861,94.249323 139.70235,95.252343 139.70235,95.252343 L 130.3586,95.252343 L 130.3586,108.37734 C 130.3586,108.37734 130.86226,124.03359 114.95235,124.03359 C 99.042448,124.03359 88.421098,124.03359 88.421098,124.03359 C 88.421098,124.03359 73.514848,123.79263 73.514848,138.43985 C 73.514848,153.08705 73.514848,162.6586 73.514848,162.6586 C 73.514848,162.6586 71.251668,177.31485 100.5461,177.31485 z M 115.2961,168.8461 C 112.63467,168.8461 110.4836,166.69503 110.4836,164.0336 C 110.4836,161.37217 112.63467,159.2211 115.2961,159.2211 C 117.95753,159.2211 120.1086,161.37217 120.1086,164.0336 C 120.10861,166.69503 117.95753,168.8461 115.2961,168.8461 z " id="path8620" style="opacity:1;color:#000000;fill:url(#linearGradient11307);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"/>\n    </g>\n  </g>\n</svg>'    
my_svg = SVG(data=data, metadata = "SVG creado por datos")
display(my_svg)
print(my_svg.metadata)
image/svg+xml
SVG creado por datos

Más información:

from IPython.display import SVG
SVG?

4 Video

4.1 Video

Video(data=None,
    url=None,
    filename=None,
    embed=False,
    mimetype=None,
    width=None,
    height=None,
    html_attributes='controls')
from IPython.display import Video
Video("2021-05-ipython-display/Python.webm",
      width=600,
      height=400)
from IPython.display import Video
Video("https://upload.wikimedia.org/wikipedia/commons/transcoded/b/b4/Ball_python_%28Python_regius%29_in_a_zoo.webm/Ball_python_%28Python_regius%29_in_a_zoo.webm.1080p.vp9.webm",
      width=600,
      height=400)
from IPython.display import Video
Video('2021-05-ipython-display/Python.webm', embed=True, html_attributes="no-controls muted autoplay", width=600, height=400)

Más información:

from IPython.display import Video
Video?

4.2 Vimeo Video

class VimeoVideo(id, 
                 width=400, 
                 height=300, 
                 **kwargs)

Un objeto Vimeo Video creado tiene los siguientes métodos o atributos:

  • iframe: String con la representación HTML del objeto.
from IPython.display import VimeoVideo, HTML
VimeoVideo(148751763, width=600, height=400)

Más información

from IPython.display import VimeoVideo
VimeoVideo?

4.3 Youtube Video

YouTubeVideo(id, 
             width=400, 
             height=300, 
             **kwargs)

Un objeto YouTube Video creado tiene los siguientes métodos o atributos:

  • iframe: String con la representación HTML del objeto.
from IPython.display import YouTubeVideo
YouTubeVideo("j9V78UbdzWI", width=600, height=400)

Más información

from IPython.display import YouTubeVideo
YouTubeVideo?

 5 Audio

5.1 Audio

Audio(data=None, 
    filename=None, 
    url=None, 
    embed=None, 
    rate=None, 
    autoplay=False, 
    normalize=True,
    element_id=None
)

Métodos del objeto (creado):

  • autoplay_attr
  • element_id_attr
  • metadata
  • reload
  • src_attr
from IPython.display import Audio
Audio("2021-05-ipython-display/Python.mp3")
Audio("https://upload.wikimedia.org/wikipedia/commons/transcoded/1/13/EN-Monty_Python-GT.ogg/EN-Monty_Python-GT.ogg.mp3",
      autoplay=True,
     )
# https://pages.mtu.edu/~suits/notefreqs.html 
from IPython.display import Audio
import numpy as np

N_secs, framerate = 2, 44100
t = np.linspace(0,N_secs,framerate*N_secs)
data = np.sin(2*np.pi*440*t) # nota musical do - C
Audio(data, rate=framerate)
from IPython.display import Audio
import numpy as np

N_secs, framerate = 1, 44100
t = np.linspace(0,N_secs,framerate*N_secs)
data = np.sin(2*np.pi*440*t)
zero = np.zeros(data.shape)
dataleft = np.concatenate([zero, data, zero, zero, data, data])
dataright = np.concatenate([data, zero, zero, data, zero, data])
Audio([dataleft, dataright], rate=framerate)

Más información:

Audio?

 6 Código o Texto

2.2 Code

Code(data=None, 
     url=None, 
     filename=None, 
     language=None)
  • Finalidad: Sirve para mostrar código de python (coloreado).
  • ¿Cuando sería util? Tienes un archivo y quieres mostrarlo en un notebook o rise, y en lugar de usar print usas esto para que tengo colores bonitos.
from IPython.display import Code
my_code = """print(" ".join(["hola","mundo"]))
a = 3
"""
Code(my_code, language="python")
print(" ".join(["hola","mundo"]))
a = 3
from IPython.display import Code
my_code = """console.log("hola" + " " + "mundo")
var a = 3
"""
Code(my_code, language="javascript")
console.log("hola" + " " + "mundo")
var a = 3
 
 

Más información

from IPython.display import Code
Code?

2.13 Latex

Latex(data=None, 
      url=None,
      filename=None, 
      metadata=None)

Métodos o atributos del objeto creado:

  • metadata: Atributo string con la metadata con la que fue creado el objeto.
  • reaload(): Método para recargar el contenido del objeto, si fue creado desde url o archivo.
from IPython.display import Latex
Latex(r"$c = \sqrt{a^2 + b^2}$ pero $\alpha > \beta$")
$c = \sqrt{a^2 + b^2}$ pero $\alpha > \beta$
from IPython.display import Latex
my_url = ""
Latex(url=my_url)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-21-a22c41619303> in <module>
      2 from IPython.display import Latex
      3 my_url = ""
----> 4 Latex(url=my_url)

~/.local/lib/python3.7/site-packages/IPython/core/display.py in __init__(self, data, url, filename, metadata)
    635             self.metadata = {}
    636 
--> 637         self.reload()
    638         self._check_data()
    639 

~/.local/lib/python3.7/site-packages/IPython/core/display.py in reload(self)
    665             # Deferred import
    666             from urllib.request import urlopen
--> 667             response = urlopen(self.url)
    668             data = response.read()
    669             # extract encoding from header, if there is one:

/miniconda3/lib/python3.7/urllib/request.py in urlopen(url, data, timeout, cafile, capath, cadefault, context)
    220     else:
    221         opener = _opener
--> 222     return opener.open(url, data, timeout)
    223 
    224 def install_opener(opener):

/miniconda3/lib/python3.7/urllib/request.py in open(self, fullurl, data, timeout)
    508         # accept a URL or a Request object
    509         if isinstance(fullurl, str):
--> 510             req = Request(fullurl, data)
    511         else:
    512             req = fullurl

/miniconda3/lib/python3.7/urllib/request.py in __init__(self, url, data, headers, origin_req_host, unverifiable, method)
    326                  origin_req_host=None, unverifiable=False,
    327                  method=None):
--> 328         self.full_url = url
    329         self.headers = {}
    330         self.unredirected_hdrs = {}

/miniconda3/lib/python3.7/urllib/request.py in full_url(self, url)
    352         self._full_url = unwrap(url)
    353         self._full_url, self.fragment = splittag(self._full_url)
--> 354         self._parse()
    355 
    356     @full_url.deleter

/miniconda3/lib/python3.7/urllib/request.py in _parse(self)
    381         self.type, rest = splittype(self._full_url)
    382         if self.type is None:
--> 383             raise ValueError("unknown url type: %r" % self.full_url)
    384         self.host, self.selector = splithost(rest)
    385         if self.host:

ValueError: unknown url type: ''

Más información

from IPython.display import Latex
Latex?

2.14 Markdown

Markdown(data=None, 
         url=None, 
         filename=None, 
         metadata=None)

Métodos o atributos del objeto creado:

  • metadata: Atributo string con la metadata con la que fue creado el objeto.
  • reaload(): Método para recargar el contenido del objeto, si fue creado desde url o archivo.
from IPython.display import Markdown
my_markdown = '''
## Enfasis

**This is bold text**

__This is bold text__

*This is italic text*

_This is italic text_

~~Strikethrough~~

## Otros
(c) (C) (r) (R) (tm) (TM) (p) (P) +-
"Smartypants, double quotes" and 'single quotes'

'''
Markdown(my_markdown)

Enfasis

This is bold text

This is bold text

This is italic text

This is italic text

Strikethrough

Otros

(c) (C) (r) (R) (tm) (TM) (p) (P) +- "Smartypants, double quotes" and 'single quotes'

from IPython.display import Markdown
Markdown("2021-05-ipython-display/markdown_example.md")

2021-05-ipython-display/markdown_example.md

from IPython.display import Markdown
Markdown("http://github/x/y/x/markdown_example.md")

Más información

from IPython.display import Markdown
Markdown?

2.15 Math

Math(data=None, 
     url=None, 
     filename=None, 
     metadata=None)

Métodos o atributos del objeto creado:

  • metadata: Atributo string con la metadata con la que fue creado el objeto.
  • reaload(): Método para recargar el contenido del objeto, si fue creado desde url o archivo.
from IPython.display import Math
Math(r'F(k) = \int_{-\infty}^{\infty} f(x) e^{2\pi i k} dx')
$\displaystyle F(k) = \int_{-\infty}^{\infty} f(x) e^{2\pi i k} dx$

Más información

from IPython.display import Math
Math?

 7 Otros

class IPython.display.FileLink(path, 
                               url_prefix='', 
                               result_html_prefix='', 
                               result_html_suffix='<br>')
  • Finalidad: Embeber un archivo local que pueda ser clickeado, basado en una ruta (path).
  • Ejemplo: Cuando tu código hace referencia a un archivo de datos o README, y quieres que pueda abrirse fácilmente. O cuando generas un output, y quieres que un usuario lo mire.
from IPython.display import FileLink
FileLink("2021-05-ipython-display/latex.txt")
from IPython.display import FileLink
FileLink("2021-05-ipython-display/latex.txt", url_prefix="url_prefix", result_html_prefix='html_prefix', result_html_suffix='html_suffix')
html_prefix2021-05-ipython-display/latex.txthtml_suffix

Más información

from IPython.display import FileLink
FileLink?
class IPython.display.FileLinks(path, 
                                url_prefix='', 
                                included_suffixes=None, 
                                result_html_prefix='', 
                                result_html_suffix='<br>', 
                                notebook_display_formatter=None, 
                                terminal_display_formatter=None, 
                                recursive=True)
  • Finalidad: Embeber un archivo local que pueda ser clickeado, basado en una ruta (path).
  • Ejemplo: Cuando tu código hace referencia a un archivo de datos o README, y quieres que pueda abrirse fácilmente. O cuando generas un output, y quieres que un usuario lo mire.
from IPython.display import FileLinks
FileLinks("2021-05-ipython-display")
2021-05-ipython-display/
  markdown.md
  Python.webm
  Python.jpg
  Python.png
  code.js
  Python.svg
  code.py
  Python.gif
  latex.txt
  Image.txt
  math.txt
  Python.mp3

Más información

from IPython.display import FileLinks
FileLinks?
from IPython.display import FileLinks
print("Methods:")
print(sorted([f for f in dir(FileLinks) if not f.startswith('_')]))
Methods:
['html_link_str']

2.17 Progress Bar

class IPython.display.ProgressBar(
    total, # int >0
)

Un objeto Progress Bar creado tiene los siguientes métodos o atributos:

  • display(): Método que permite desplegar el objeto directamente.
  • progress: Atributo int >0 que permite cambiar el progreso realizado, int >= 0.
  • update(): Método que permite actualizar el display.
  • metadata: Atributo string de la metadata con la que fue creado el objeto.
  • Me parece que reload() no se usa o no aplica en este caso.
from IPython.display import ProgressBar
pb = ProgressBar(100)
pb.progress = 42
pb.display()
from IPython.display import ProgressBar
import time

pb = ProgressBar(100)
pb.display()
for i in range(0,101,5):
    pb.progress = i # No es necesario actualizar con pb.update(), solo modificar el valor
    time.sleep(.1) # Opcional

Más información:

from IPython.display import ProgressBar
ProgressBar?

2.19 Scribd Document

ScribdDocument(id, 
               width=400, 
               height=300, 
               **kwargs)

Un objeto Scribd Document creado tiene los siguientes métodos o atributos:

  • iframe: String con la representación HTML del objeto.
from IPython.display import ScribdDocument
ScribdDocument(56131068, width=300, height=400)

Más información

from IPython.display import ScribdDocument
ScribdDocument?

8. Sin representación gráfica directa

 2.3 Display Handle

class IPython.display.DisplayHandle(display_id=None)
  • Funcionalidad: Ni idea... ¿para actualizar un display??
  • Ejemplo: ??
from IPython.display import DisplayHandle
DisplayHandle(1) # ?
<DisplayHandle display_id=1>

Más información

from IPython.display import DisplayHandle
DisplayHandle?
from IPython.display import DisplayHandle
print("Methods:")
print(sorted([f for f in dir(DisplayHandle) if not f.startswith('_')]))
Methods:
['display', 'update']

2.4 Display Object

.. ..

Más información

from IPython.display import DisplayObject
DisplayObject?
from IPython.display import DisplayObject
print("Methods:")
print(sorted([f for f in dir(DisplayObject) if not f.startswith('_')]))

2.7 GeoJSON

GeoJSON(data : dict or list VegaLite data. Not an already-serialized JSON string.
        url_template : string - Leaflet TileLayer URL template
        layer_options : dict - Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
        url : unicode - A URL to download the data from.
        filename : unicode - Path to a local file to load the data from.
        metadata: dict - Specify extra metadata to attach to the json display object.
       )

Métodos o atributos del objeto creado:

  • data:
  • metadata:
  • reaload:

Mostrar un json geo

... no me funciona??

from IPython.display import GeoJSON

GeoJSON(data={
         "type": "Feature",
         "geometry": {
             "type": "Point",
             "coordinates": [-81.327, 296.038]
            }
 },
 url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
 layer_options={
     "basemap_id": "celestia_mars-shaded-16k_global",
     "attribution" : "Celestia/praesepe",
     "minZoom" : 0,
     "maxZoom" : 18,
 })

Más información:

from IPython.display import GeoJSON
GeoJSON?
from IPython.display import GeoJSON
print("Methods:")
print(sorted([f for f in dir(GeoJSON) if not f.startswith('_')]))

2.11 JSON

JSON(
    data=None,
    url=None,
    filename=None,
    expanded=False,
    metadata=None,
    root='root',
    **kwargs,
)
from IPython.display import JSON
JSON({"name":"John", "age":31, "city":"New York"})

Más información

from IPython.display import JSON
JSON?
from IPython.display import JSON
print("Methods:")
print(sorted([f for f in dir(JSON) if not f.startswith('_')]))

2.16 Pretty

Pretty(data=None, 
       url=None, 
       filename=None, 
       metadata=None)

Métodos o atributos del objeto creado:

  • metadata: Atributo string con la metadata con la que fue creado el objeto.
  • reaload(): Método para recargar el contenido del objeto, si fue creado desde url o archivo.

Más información

from IPython.display import Pretty
Pretty?
from IPython.display import Pretty
print("Methods:")
print(sorted([f for f in dir(Pretty) if not f.startswith('_')]))

2.20 TextDisplayObject

TextDisplayObject(data=None, 
                  url=None, 
                  filename=None, 
                  metadata=None)

No parece tener un display visual para jupyter notebook/lab, es más bien una estructura genérica.

Un objeto Text Display Object creado tiene los siguientes métodos o atributos:

  • metadata: Atributo string con la metadata con la que fue creado el objeto.
  • reaload(): Método para recargar el contenido del objeto, si fue creado desde url o archivo.
from IPython.display import TextDisplayObject
TextDisplayObject("Hmm, para que será esto??")

Más información

from IPython.display import TextDisplayObject
TextDisplayObject?
from IPython.display import TextDisplayObject
print("Methods:")
print(sorted([f for f in dir(TextDisplayObject) if not f.startswith('_')]))
Las 3 lecciones de los CEOs del Silicon Valley que valen un billón de dólares | sebastiandres

Las 3 lecciones de los CEOs del Silicon Valley que valen un billón de dólares

¿Tan simple como sé vulnerable, evita la democracia y trabaja en equipo?

fastpages   markdown

Trillion Dollar Coach, traducido al español como “El coach de Silicon Valley”, es un libro escrito por Eric Schmidt, Jonathan Rosenberg y Alan Eagle, donde relatan los aprendizajes y consejos que recibieron de Bill Campbell, un coach con un estilo personal que rompía esquemas y convenciones.

Bill Campbell (1940–2016) fue un entrenador de fútbol americano en su juventud, después CEO de empresas, y finalmente coach para los gerentes de múltiples empresas en el Silicon Valley: Google (Eric Schmidt, Larry Page, Sergey Brin, Dundar Pichai, Jonathan Rosenberg, Alan Eagle), Amazon (Jeff Bezos), Facebook (Sheryl Sandberg), Apple (Steve Jobs), Twitter (Jack Dorsey), entre muchos otros. Todos sus “alumnos” le atribuyen gran parte de su éxito a sus enseñanzas y consejos, por lo si sumamos la capitalización bursátil de todas estas empresas merece ampliamente el título del libro: más de 1,000,000,000,000 dólares (billón en español, trillion en inglés).

Al leer el libro, no pude evitar acordarme de aquellos profesores y jefes que han marcado una gran diferencia en mi vida. Todos compartían las características atípicas que se mencionan en el libro como esenciales para un buen jefe: cercanía y autenticidad, retroalimentación honesta pero constructiva, creer en tu potencial y empujarte a abordar desafíos. Dicho así, suena trivial, son todas virtudes positivas y que buscamos desarrollar en nosotros como personas o como jefes. Pero la verdad es que ser jefe no es tan sencillo. Son más los momentos en los que no sabes cuál es la decisión correcta que aquellos en los que tienes alguna certeza. A menudo tienes que tomar decisiones que afectarán a tu equipo y a tus clientes. Personalmente, tiendo a establecer fronteras muy estrictas entre distintos ámbitos de mi vida, y así, por ejemplo, la gente del trabajo no conoce a mis amigos cercanos o familia más allá de mi esposa e hijos. Mis amigos de mi universidad de pregrado no se conocen con mis amigos de universidad de posgrado. Es un desafío constante vencer la tentación de no “compartimentalizar” los distintos aspectos de mi vida, y borrar fronteras que sólo existen en mi mente. Tratamos de no exponer nuestros miedos y vulnerabilidades, de no establecer conexiones personales fuera de lo profesional, porque creemos que nos permitirá tomar mejores decisiones, en frío, sin dejarse dominar por emociones. Como si la conexión y la empatía nos volvieran más débiles. Sin embargo, el libro y diversas investigaciones recientes muestran lo contrario. Un gran jefe se parece a un gran padre: entrega soporte emocional e intelectual, escucha y aconseja, pero también confía en tu potencial y te motiva para que lo alcances.

¿Preferirías ser un padre frío y no amar a tu hijo, para tomar mejores decisiones respecto a su alimentación, educación, salud o seguridad? Por supuesto que no, porque amar hace que ser padre sea el mejor trabajo del mundo, y porque así permite tomar las mejores decisiones pensando en el bienestar del otro más que en tí mismo.

Un segundo aprendizaje sorprendente del libro fueron las afirmaciones “el consenso lleva a malas decisiones” y “hay que evitar la democracia en las reuniones”. Las frases me dejaron perplejo, pero la explicación es simple y contundente. Si en una reunión, la decisión la toma el gerente de mayor rango o se realiza votación, entonces el asunto se vuelve un tema de quién hace mejor lobby para que elijan su opinión. Al contrario, el libro propone que un buen proceso de toma de decisiones tiene ciertas características específicas. Debe ser liderado por la persona más experta en el tema. Luego todos deben opinar, especialmente aquellos que tengan una opinión diferente. Todos deben poder preguntar y preguntar hasta que no queden dudas. Al final, y sólo entonces, puede opinar el gerente de mayor rango para evitar orientar la discusión en una dirección específica. El equipo en su conjunto elige la mejor decisión basada en los méritos de la idea y no de quién lo propone. Sólo en caso de empate, y si el equipo no es capaz de resolver la diferencia por sí mismo, es necesario que el CEO tome una decisión. Una vez tomada la decisión, todos deben respaldarla. Sin agenda privada, sin sabotearla y sin rencores. La decisión la tomó el equipo y la deben respaldar todos sus integrantes. Un detalle adicional: las reuniones se realizan para tomar decisiones. Si en una reunión no van a tomar decisiones, ¡no es necesario tener dicha reunión!

Un tercer aspecto a destacar: muchos de los consejos del libro están alineados con la metodología SCRUM, aunque esto no se menciona de manera directa. SCRUM es una metodología de trabajo que se usa en proyectos, desarrollo de software y hasta en marketing, porque permite entregar resultados de manera rápida para así ir validando que la solución entregue una respuesta al problema, y vaya así mejorando semana a semana. Recibe su nombre del scrum del rugby, porque busca generar compromiso y unión entre los integrantes del equipo, y que de manera conjunta empujan hasta llegar a la meta. La primera similitud entre el libro y SCRUM radica en la vital importancia que tiene el equipo. Al igual que en SCRUM, para Bill el éxito de una empresa pasa por tener grandes equipos de personas, trabajando comprometidas como una unidad. El bienestar y éxito del equipo debe ser el foco de todos sus integrantes. Los equipos deben ser multidisciplinarios y tener a las mejores personas, con la mayor variedad posible. A continuación, mencionaré la relación entre los pilares y valores de la metodología SCRUM y el libro (no es requisito conocer SCRUM previamente).

En relación a los pilares SCRUM:

  • Transparencia: la honestidad y transparencia aparece como motivo recurrente del libro, tanto en las relaciones entre integrantes del equipo, como con jefes y con la junta directiva.
  • Inspección: Bill tenía un checklist para las entrevistas individuales para inspeccionar 4 dimensiones fundamentales en una evaluación: desempeño en el rol, relación con pares, liderazgo y dirección, e innovación. En cada una, era fundamental contar con métricas objetivas para iniciar una conversación.
  • Adaptación: un aprendizaje valioso del libro fue ver que incluso los grandes CEOs se arrepienten de sus decisiones y cambian su decisión inicial. Necesitamos perder la arrogancia de pensar que siempre sabremos qué hacer. En función de lo que vamos aprendiendo, tendremos que ir adaptando nuestras elecciones.

En cuanto a los valores SCRUM:

  • Compromiso: de manera literal, en varias ocasiones el libro menciona la importancia de comprometerse con lograr los objetivos planteados y de apoyarse mutuamente.
  • Enfoque: Bill siempre preguntaba por el problema principal, y su preocupación era asegurarse de tener a las mejores personas en el equipo, y revisar los procesos. Dejar que las personas inteligentes puedan hacer su trabajo sin distracciones.
  • Apertura: Bill entregaba y exigía honestidad directa y sin rodeos, que permita determinar sin dudas el estado de un problema. Lo mismo aplica para el feedback: los reproches deben ser en privado usando retroalimentación constructiva, y las felicitaciones públicas.
  • Respeto: comienza con el reconocimiento de la humanidad fundamental de los demás. Todos los empleados poseen un trasfondo cultural, familiar y emocional fuera del trabajo. Esto debe ser conocido, aceptado e incluso fomentado para crear un ambiente de trabajo que cultive una red de confianza para criticar y disentir. Que la gente esté empoderada para manifestar su desacuerdo es una herramienta imprescindible en una organización. Acallar, humillar, o cualquier tipo de discriminación no debe ser tolerado, porque generará un ambiente tóxico.
  • Coraje: No ser conformista. Hacer lo correcto. Tener conversaciones incómodas. Aventurarse a trabajar en los problemas significativos aunque sean complejos.

Al igual que estos valores y pilares de SCRUM, Bill buscaba generar una confianza profunda entre los integrantes de los equipos. Los fanáticos del fútbol (el real, no el americano :) se habrán percatado que la foto del artículo es de Marcelo Bielsa, entrenador de la selección chilena de fútbol masculino entre 2007 y 2011. No tengo dudas que Marcelo y Bill se hubieran entendido a la perfección, y que desarrollaron la misma filosofía: el valor del trabajo duro y honesto, la búsqueda de equipos comprometidos y enfocados, la preocupación obsesiva por el proceso sobre el resultado específico, y la persecución del éxito con dignidad y sin trucos.

El detective: entre la lógica y el azar | sebastiandres

El detective: entre la lógica y el azar

Las raíces de la serendipia y la novela policial clásica en un cuento persa

policial   comentario

Cuando pensamos en Sherlock Holmes, nos imaginamos al famoso detective resolviendo el misterio desde la comodidad de su casa, sin dejar de fumar su pipa o derramar una gota de su té de Ceylán, gracias a su portentoso cerebro. No hay nada de suerte en su implacable y fría lógica… en apariencia.

El término serendipia viene del inglés serendipity. No es por azar que suene mejor en español que en inglés, si consideramos que proviene de serendip, una palabra de origen árabe al igual que tantas otras de igual origen que han enriquecido el idioma español. El escritor y político inglés Horace Walpole acuñó la palabra serendipity en honor al cuento “Three Princes of Serendip” hace más de 250 años. El reino de Serendip que da origen al cuento, actualmente Sri Lanka o Ceylán, es una isla al sur de India. En el relato, tres príncipes son injustamente acusados de un robo, al haber deducido múltiples cualidades de ciertos objetos robados. No daré más detalles para no arruinar una historia entretenida que recomiendo leer. La palabra serendipia terminó entonces denominado un “descubrimiento afortunado e inesperado que se logra cuando se está buscando una cosa distinta”, o como diríamos más informalmente, un chiripazo. Se citan como ejemplos de serendipias, el descubrimiento de la Penicilina, de los post-its, el principio de Arquímides y el Tefón, entre otros.

Sin embargo en el cuento de los tres príncipes de Serendip, no interviene la suerte: los hermanos describen las cualidades de lo robado mediante un proceso de deducción no muy distinto al que usarían otros famosos detectives. Más aún, hay quienes postulan que dicho cuento fue fuente de inspiración para Voltaire en su novela Zadic, que a su vez, podría haber inspirado a Edgar Allan Poe para “Asesinatos de la Rue Morgue”, iniciando el género de la novela policial.

De este mismo cuento, surgen entonces 2 dimensiones contrapuestas: por una lado, la suerte y, por el otro lado, la lógica. La disonancia es sólo aparente. Consideremos un extracto de “El sabueso de los Bakersville”:

Estamos entrando en el terreno de las conjeturas -dijo el doctor Mortimer.

Digamos, más bien, en el terreno donde sopesamos posibilidades y elegimos la más probable. Es el uso científico de la imaginación, pero siempre sobre una base material sobre la que apoyar nuestras especulaciones.- respondió Sherlock.”

El sabueso de los Baskerville — Arthur Conan Doyle.

Sherlock (y todo buen detective) sabe que para una misma evidencia pueden existir múltiples explicaciones, algunas más plausibles que otras. Las probabilidades le permiten saber cuál es la causa más probable, y nueva evidencia le permite confirmar o descartar algunas de las hipótesis. Es el método científico en su plena expresión.

Supongamos que para un crimen existen 4 posibles culpables con una cierta estimación de culpabilidad considerando la evidencia actual: Alicia (24%), Bob (24%), Carlos (49%) o David (3%). Sin duda nos inclinaríamos por creer que Carlos es el culpable pues tiene más del doble de probabilidad que los otros 3 sospechosos. Sin embargo, es más probable que no sea culpable (24%+24%+3%=51%) a que sí lo sea (49%). Por supuesto, no podemos aún probar la culpabilidad de Carlos, pero el curso de acción más razonable es investigar más profundamente a Carlos para confirmar o refutar la hipótesis de su culpabilidad. Si finalmente se encuentra más evidencia y acertamos en que Carlos era el culpable, hemos de admitir entonces que hemos tenido un grado de suerte pues otros escenarios eran también admisibles (incluso más probables). Es la mezcla de análisis deductivo, búsqueda dirigida y suerte la que termina por llevar al éxito.

En resumen, existe serendipia o chiripa en el proceso deductivo. Si bien se elige mediante lógica la opción más probable, y sobre ésta se prosigue la investigación hasta agotar posibilidades, existe algo de suerte en que la explicación inicial sea la correcta, en lugar de otras menos probables (con la información inicial), y en el descubrimiento de nuevas pistas y evidencias.

Como dijo Louis Pasteur: “La suerte favorece a la mente preparada”.

Los Tres Príncipes de Serendip | sebastiandres

Los Tres Príncipes de Serendip

Un antiguo y desconocido cuento persa de deducciones, que muestra porqué no deberíamos ufanarnos de nuestro conocimiento.

policial   cuento

Érase una vez, en el reino de Serendip, un sabio y poderoso rey llamado Giaffer. Tenía tres hijos a los que amaba mucho, y quería dejarles, no sólo su reino, sino que todos los conocimientos y virtudes que debían tener los gobernantes de un gran reino. Así que reunió a grandes eruditos de todo su reino, cada uno con una especialidad diferente, y los puso como tutores de sus hijos. El rey pidió a cada uno que instruyera a los príncipes tan bien que cualquier experto que los encontrara reconocería de inmediato quién era su maestro. Y así lo hicieron los tutores.

Debido a que todos los príncipes eran muy inteligentes, les llevó poco tiempo convertirse en expertos en ciencia, lenguaje, filosofía y todas las demás materias que estudiaron, y pronto estaban mucho más informados que cualquier otro joven príncipe o noble del mismo país, edad y rango. Los tutores informaron al rey sobre el gran progreso que habían hecho los príncipes. El rey estaba un poco escéptico de que los príncipes pudieran haber adquirido tanto conocimiento tan rápido, así que decidió ponerlos a prueba.

En reunión privada con su hijo mayor, el rey le dijo: “Hijo mío, sabes cuánto tiempo he gobernado este reino y cuánto me he esforzado por cuidar a mis súbditos y gobernarlos con amor y misericordia, así como con justicia. Pero ahora estoy envejeciendo y siento que es hora de concentrarme en mi viaje al otro mundo. He decidido retirarme a un monasterio, para pasar el resto de mis días meditando y orando por mis pecados. Eres mi hijo mayor, y por eso te dejo el reino.”

El hijo mayor se inclinó ante su padre y dijo: “Padre y rey mío, aunque comprendo sus deseos, sé que todavía puede gobernar bien este reino y, con la gracia de Dios, oraré para que pueda hacerlo por muchos más años. Aunque estoy dispuesto a obedecerle en todo lo que me pida, mientras usted esté vivo y saludable, no sería apropiado que yo lleve la corona. Sólo cuando asciendas a tu merecido lugar en el cielo, me ocuparé del reino y trataré de gobernar con tanta sabiduría y justicia como tú ”.

El rey estaba complacido con la respuesta de su hijo, que mostró sabiduría y humildad. Pero ocultó sus sentimientos y le dijo a su hijo que se retirara. Luego llamó a su segundo hijo y le hizo la misma propuesta. El segundo hijo también se negó y agregó: “¿No debería mi hermano mayor ser el gobernante después de ti?” El rey volvió a ocultar sus sentimientos y llamó a su tercer hijo, quien también se negó, recordándole a su padre que sus dos hermanos mayores tenían precedencia en el trono antes que él.

El rey estaba satisfecho con los aprendizajes de sus hijos. Decidió que, para completar su educación, los haría viajar por el mundo para que tuvieran experiencias de la vida real además de los conocimientos adquiridos de los libros y sus maestros. Llamó a sus hijos y fingiendo estar enojado, les dijo: “Me han desobedecido y se negaron a mis deseos. Están expulsados del reino. Salid.”

Los príncipes estaban sorprendidos y herido, pero amaban a su padre, y él era el rey, así que los tres recogieron algunas cosas y dejaron el reino. Continuaron viajando hasta llegar a otro reino, gobernado por un gran emperador llamado Beramo. Mientras viajaban por la carretera hacia la capital imperial, se encontraron con un comerciante cuyo camello se había escapado. El comerciante preguntó a los príncipes si habían visto su camello en el camino.

El primer hijo le preguntó: “¿Tu camello estaba ciego de un ojo?”

“Pues sí,” dijo el hombre.

“¿Y le faltaba un diente?” preguntó el segundo hijo.

“Sí”, dijo el hombre.

“¿Y también estaba cojo?” preguntó el tercer hijo.

“¡Sí!” el hombre dijo.

“¿Su camello tiene una carga de mantequilla en un lado y una carga de miel en el otro?” preguntó dijo el primer hijo.

“¡Así es!”, contestó el comerciante.

“Además, lleva a una mujer en la espalda”, dijo el segundo hermano.

“Y ella está embarazada”, agregó el tercer hermano.

“Sí, ese es mi camello y mi mujer” dijo el hombre.

“Pues no lo hemos visto” dijeron los príncipes y continuaron su rumbo.

El comerciante pensó que la única forma en que los hermanos podrían haber sabido tanto sobre el camello desaparecido que lo hubieran robado y escondido ellos mismos. Entonces el hombre fue al juez, acusando a los hermanos de haberle robado su camello, y el juez hizo que los arrestaran y los metieran en la cárcel. Al día siguiente, el propio emperador escuchó el caso y los condenó a muerte.

Por suerte para ellos, alguien encontró al camello y la mujer, dando aviso al comerciante. El hombre volvió a la corte y con humildad reconoció haber encontrado su camello y que los tres hombres eran inocentes. El emperador los hizo soltar y los llamó a su presencia. Preguntó a los hermanos cómo habían conocido tantos detalles sobre un camello que nunca habían visto.

El primer hermano dijo: “Me di cuenta de que la hierba sólo se había comido en un lado del camino, aunque la hierba del otro lado era de mejor calidad. Así que llegué a la conclusión de que el camello no debía ver bien del lado del camino donde la hierba era buena”.

El segundo hermano dijo: “Me di cuenta de que entre la hierba estaba cortada a ras, pero que quedaban algunas briznas completamente sin morder, por lo que el camello debía tener un diente faltante”.

El tercer hermano dijo: “Y supe que el camello debía estar cojo, porque observé las huellas de tres patas de camello junto con la huella de un pie que se arrastraba”.

El emperador estaba asombrado e impresionado, y quería saber más. “¿Cómo supieron los otros detalles?” les preguntó.

El primer hermano dijo: “Supuse que el camello debe llevar mantequilla por un lado y miel por el otro, porque en un lado del camino noté un rastro de hormigas, que aman la grasa, y en el otro lado noté un gran número de moscas, que aman la miel”.

El segundo hermano dijo: “Supuse que el camello llevaba a una mujer, porque en un momento vi marcas que indicaban que el camello se había arrodillado, y cerca vi una pequeña huella humana, que podría ser una mujer o un niño. Había orina cerca, y cuando la olí, sentí un arrebato de lujuria, así que estaba seguro de que la huella debía ser la de una mujer”.

El tercer hermano dijo: “Y supuse que la mujer debía estar embarazada, porque vi huellas de manos, lo que indica que la mujer tenía que ayudarse a levantarse con las manos después de orinar”.

El emperador quedó tan impresionado con la inteligencia y las habilidades de observación de los hermanos que les rogó que se quedaran como sus invitados por un tiempo. Les proporcionó las mejores habitaciones del palacio, y todos los días se entretenía con ellos, discutiendo una variedad de temas diferentes y disfrutando de su inteligente conversación.


Traducido y editado con libertad de esta versión del cuento en inglés: link.

Este cuento persa es considerado uno de los primeros cuentos de deducción lógica. En comento más al respecto.

Crimen a la carta | sebastiandres

Crimen a la carta

Taller de narrativa policial

escritura

Felipe terminó de leer su cuento y se produjo un silencio incómodo. El taller de narrativa policial cerraba la última clase con las lecturas de los alumnos, y varios habíamos compartido nuestras historias. Me alegré de haber leído mi relato antes que él; todavía sentía la respiración agitada y la adrenalina, y me habían dado buenas ideas para mejorar el suspenso. Felipe era uno de los integrantes del taller que me causaba más curiosidad: en algunas clases participaba de manera activa, cercana y chispeante, mientras que en otras evitaba participar aunque se veía atento a todo lo que se comentaba. No sabíamos mucho de él, no quiso comentar su profesión o trabajo, y solo dijo que estaba desde siempre obsesionado con las novelas policiales. El silencio tras la lectura se prolongó tanto que por un momento pensé que mi internet estaba fallando de nuevo. En la clase anterior mi computador se había quedado pegado dos veces, pero esta vez el internet no era el problema ahora. Podía ver como algunos de los asistentes parpadeaban o se acomodaban en sus asientos. Creo que nadie se atrevía a decir lo evidente: el cuento, sencillamente, no había sido bueno. La trama no se sostenía, era rebuscado y sobre todo, estaba muy mal narrado. En resumen, la historia de Felipe era plana y sin adornos: 2 detectives jugaban un juego de mesa mientras discutían su último caso: un envenenamiento por cianuro. Inicialmente se había sospechado suicidio, porque la víctima vivía sola y no recibía visitas, pero después las pistas revelaban que el veneno había llegado en una comida entregada por Uber Eats. De la historia había tan poco por rescatar que incluso la profesora se veía incómoda, buscando las palabras para dar algún comentario alentador:

- Tiene potencial, Felipe – le dijo – hay, ehhm, ideas novedosas ¿Tú eres de Rancagua, verdad? Quizás podrías situar las escenas en algún lugar que conozcas bien de Rancagua, para describir un ambiente de manera más realista.

La sugerencia no era descabellada. Felipe no había descrito donde estaban los personajes. La historia sólo decía que estaban jugando alguna especie de juego de mesa, pero nada indicaba el año o la hora, si era una casa, departamento o cuarto de hotel. Toda la historia estaba narrada de manera demasiado directa. Para peor, los personajes rayaban en el cliché, unas copias nada disimuladas de Sherlock y Watson. Más que un cuento, parecía un recetario de crimen paso a paso.

- ¿Qué piensa el resto de la clase? - preguntó la profesora, buscando apoyo en el grupo.

Yo ya sabía que el primero en opinar sería Alejandro. Era ese tipo de personas que no tenía problema en interrumpir y dar su opinión con lujo de detalles, aunque alguien más ya estuviera hablando, como ya había comprobado en las sesiones anteriores.

- Felipín, Felipín – dijo Alejandro – ¿Por donde empiezo? Primero que nada, ¿Por qué los detectives tienen que estar jugando a ese juego de mesa desconocido? ¿Cómo se llamaba?

- El juego se llama go, Alejandro. – le contestó Felipe con un tono que no escondía su molestia – Es un juego milenario, de origen japonés. Los jugadores van colocando alternadamente sus fichas de color blanco o negro, y gana quien logra dominar una mayor superficie.

- Me parece que es una mala elección poner un juego que resulta desconocido y que además no se explica bien - le asestó Alejandro.

Felipe suspiró profundamente. No parecía incómodo; todavía miraba fijamente la pantalla. Más bien parecía cansado, quizás arrepentido de habernos compartido su cuento. Hizo una pausa, y le respondió sobre que el juego de la historia es una metáfora de la competencia entre policías y ladrones. Cada nueva tecnología abre nuevas posibilidades de crímenes. Se crean las impresoras 3d y al rato ya existen modelos para imprimir las piezas para armar una pistola. Se crean las redes sociales y al segundo aparecen los perfiles falsos y el bullying. Es una carrera sin fin. Es interesante ver cómo van apareciendo nuevos tipos de crímenes que antes no eran posibles.

- Ya veo, el concepto no es taaaaaan malo una vez que lo explicas – concedió Alejandro - pero no es nada de lo que cuentas en tu historia, sólo dices que juegan ese juego de mesa, nada del trasfondo. Por otra parte, no se entiende bien el crimen. ¿Cómo logró obtener la password de su cuenta de Uber Eats para saber exactamente lo que había ordenado y sustituirla?

- No me pareció interesante contarlo – respondió Felipe – pensé que sería muy obvio. Existen muchas formas de conseguir una contraseña. La manera más fácil de conseguir una es, por ejemplo, armar un sitio web con alguna promesa vacía a todas personas que se hagan una cuenta. La gran mayoría de las personas son tan flojas que usan una única contraseña para todas sus plataformas en internet: facebook, instagram, correo, banco, zoom, etc.

Varias de las pequeñas caras en mi pantalla abrieron los ojos sorprendidos, lo que me hizo pensar que no era el único que se había sentido identificado. En efecto, bastaba que cualquiera de los cientos de sitios que usaba fuera falso o hubiera sido hackeado para comprometer todas mis cuentas. Tendría que al menos tener claves distintas para mis bancos, pensé.

- ¿Alguien más quiere comentar? – preguntó la profesora - ¿Qué opina nuestro experto?

El experto era un prefecto general de la PDI que apoyaba en algunas de las clases. En situación de retiro, como aclaraba él. Sus comentarios eran esperados con ansias. No era del tipo de personas que se iban con rodeos, y sus aclaraciones técnicas del sitio del suceso nos habían ayudado muchísimo a dar veracidad a los relatos. Bueno, a casi todos los relatos.

- Otras personas pueden emitir un juicio más certero que el que yo podría entregar sobre la calidad literaria. Me referiré por tanto a los aspectos técnicos de la narración, donde existe un error cuando se habla de la comparación de huellas digitales que permite resolver el crimen. Felipe, le recuerdo que huellas dactilares son aquellas marcas involuntarias dejadas por la sudoración y grasa de las yemas de los dedos, mientras que las impresiones dactilares corresponden al registro voluntario de éstas. No se comparan huellas dactilares, se compara una huella dactilar con una impresión dactilar - sentenció el ex-prefecto.

A pesar de verlo en un recuadro pequeño de mi pantalla, podía notar que Felipe cada vez estaba más rígido y concentrado. Como su cuento, era difícil leer sus emociones; no podría decir que se veía incómodo, pero tampoco parecía contento. Claramente no esperaba este tipo de comentarios. Por mi parte, más que los comentarios anteriores, me parecía que la historia tenía un gran problema.

- Felipe, hay algo que no entiendo – le dije - por lo que entendí del cuento, el criminal no conocía a la víctima. ¿Porqué se cometía entonces el crimen? ¿Porqué lo había elegido entre otras personas? ¿Cuál era el motivo?

- ¿Y por qué debería existir un motivo? - me respondió - podría haber elegido a cualquier persona de las que había conseguido una contraseña válida. Un móvil no es necesario – me respondió con una voz cada vez más impersonal – un criminal sin móvil tiene menos razones para ser descubierto. Un verdadero criminal no necesita más motivo que probar que puede hacerlo sin que lo atrapen.

- Pero eso es poco satisfactorio para el lector – intervino la profesora – es demasiado plano. No existe un arco argumental ni cierre a la historia. La novela policial no es una competencia de quién puede crear el crimen perfecto. Existe una historia porque el crimen quiebra la normalidad y racionalidad. El mundo del policial es oscuro, visceral, violento, irracional. El detective resuelve el crimen de una manera lógica y así restituye el orden. Se necesita ese contraste, ese juego de sombra y luz. Si todo es lógico, limpio, ordenado, no existe una restitución. El crimen podría volver a cometerse.

- Quizás tienes razón – le respondió Felipe – quizás literariamente no es atractivo. Pero la realidad no tiene porqué ser literalmente atractiva. El verdadero crimen es cien por ciento intelectual, no es pasional ni visceral. Pero no lo entenderían – insistió. Y luego colgó.

Hubo otro largo silencio incómodo. Nos miramos sin decir qué decir. Hubo algunas risas nerviosas, y la profesora le pidió al siguiente tallerista que leyera su relato. Pero algo se había quebrado en la dinámica del grupo. Noté que todos estaban más reservados, incómodos, y terminamos la sesión antes de lo normal. Traté de ignorarlo durante los días siguientes, pero me sorprendía a momentos pensando en la historia y en el comportamiento de Felipe en el taller. Algo me causaba preocupación. Una semana más tarde en la prensa apareció la noticia: un caso de envenenamiento por cianuro en Rancagua, que se sospechaba entregado por un Uber Eats. El primero de una serie de muertes sin sentido. Puedo asegurarles que no hay nada tan perturbador como haber conocido a un asesino serial. Parecen personas normales.

Trabajando en el SML | sebastiandres

Trabajando en el SML

Taller de narrativa policial

escritura

La puerta del vagón de metro se abre puntualmente a las 9:37 pm en la estación Puerto. Sergio espera tranquilamente, y sube en los últimos segundos, como si diera lo mismo quedarse o ir a algún sitio. No recuerda haber tenido un día tan difícil como el de hoy, y todavía le falta al menos una hora para llegar a casa. No es necesario esperar mucho tiempo para que comience la venta informal de alfajores y cuchuflís caseros, sánguches de jamón-queso o hamburguesas veganas. Sergio sabe que tendrán que pasar algunas horas y al menos un vaso de alcohol antes de que su estómago se recupere. Hoy ha sido un día particularmente intenso en el Servicio de Medicina Legal de Valparaíso. Comenzó su turno de 12 horas a las 8 am con los borrachos del puerto. Normalmente esos casos llegan durante la noche y se analizan antes de su turno. Esta vez y como ha sido la tónica de los últimos meses, el trabajo se acumula y desborda al turno que sigue. Pero no ha sido eso lo que le ha revuelto el estómago. Ya se ha acostumbrado al hedor a alcohol, orina y mierda, a sus cuerpos desangrados tras peleas callejeras o bañados en vómito en un último reflejo del cuerpo antes de rendirse a la intoxicación. Tampoco lo afectaron los otros cuerpos destrozados que llegaron durante el día: el abuelo, el atropellado, quemados y un largo etcétera. El día no había sido distinto de cualquier otro en la morgue hasta el cuerpo de esa adolescente de 12 años que llegó a las cuatro de la tarde. La morgue. Nunca le gustó ese nombre. Como francés. Como el tatuaje que tenía esa chica muerta cuya imagen sobre la tabla de autopsia todavía le perturbaba: Aujourd’hui. ¿Porqué una palabra tan larga cuando un simple “hoy” hubiera bastado, y sobre todo porqué el tatuaje era evidentemente tan reciente?

Trata de no pensar y no recordar. Mira la gente que subía y bajaba del vagón. Envidia su despreocupación y alegría. Desde que había comenzado su trabajo como asistente de médico de autopsia, se sentía cada vez más solitario y apartado. Aunque antes de salir del SML ya se había dado un baño completo y refregado con jabón después de su turno, y está usando ropa completamente distinta, siente todavía el olor a muerte pegado a sus fosas nasales. Sergio busca indicios de que ese olor no está solo en su cabeza. Examina los gestos de los pasajeros que rehúyen el contacto visual, no por lo que huelen, sino por esa mirada vacía y tenebrosa que los intimida. Trata de no seguir pensando en aquello, pero la fragilidad del cuerpo humano le es aterradora: el cuerpo, a pesar de su exterior pulcro y limpio, no es más que una piñata de sangre, músculos y vísceras en un equilibrio químico primario. Cuántas veces no le había tocado abrir un cadáver producto de un accidente para descubrir un cáncer que también lo hubiera matado en menos de un año. Sin embargo, las personas siguen su día a día sin la preocupación de las innumerables maneras que tienen de morir. Miraba sus caras, pensando que con seguridad alguno de ellos estaría en el frío mesón metálico de la autopsia antes de que terminara el año. Se preguntaba si ese cinismo y frialdad desaparecería en algún momento, o si ya había tomado un camino en el cual ya no había vuelta atrás. La ignorancia es felicidad, pensaba, y cuanto daría por olvidar esas imágenes que volvían sin remedio a su cabeza.

Microcuentos 2020 | sebastiandres

Microcuentos 2020

Cuentos enviados a Santiago en 100 palabras

escritura

Cocina neoliberal

Sale humo del sartén, donde trato de freir los huevos en las lagunas disparejas de aceite. La olla de arroz, inestable sobre el fuego, se cocina de manera desigual. Tengo que cocinar con ese molesto clic-clac de las cucharas de metal. Pero no hay remedio, es el resultado de salir a cacerolear cada día hasta la cuarentena. La vida a veces, y sobretodo últimamente, nos pone aprueba.

Cana

Todavía escucho el ruido de pasos y metales por las noches. Pagué mi deuda con la sociedad, pero no con Dios. Apenas salí de prisión, estuve obligado a encerrarme a hacer cuarentena por varios meses más.

Gatos

Después de extensos y precisos cálculos matemático, y considerando la cantidad de pelo que encuentro en la cama, las sillas, detrás de muebles y por todos lados, puedo decir que en esta casa viven al menos 17 gatos, y no los 2 que me quieren hacen creer.

La feria

Los sábados me levanto temprano. Entre las 7 y las 8 de la mañana se vive el tras bambalinas de la feria. El casero del champiñón que estuvo precioso dos meses pero nunca dijo porqué. Los caseros que perdieron a una hija de cáncer. La nueva casera del jugo de granada que se separó de Albert - al que llamamos de cariño Einstein. La casera que cada semana me avisa si trae maracuyá. Y el casero con la talla siempre a flor de piel y experto comerciante gritando “Lleve el Kale, tiene más propiedades que’l hijo ‘e la Bachelé”.

El regalo de cumpleaños

Siempre había sido entretenido que todos en la casa - mis papás, mi hermana y yo - estábamos de cumpleaños en abril. La primera festejada fue mi mamá, y entre todos nos esforzamos en comprarle una caja grande de esos chocolates finos. Días después nos pilló la cuarentena, sin posibilidad ni dinero para comprar más regalos. El segundo cumpleañero, mi hermana, recibió la misma caja, pero con menos de la mitad de los chocolates. Sólo espero que en mi turno, el último de todos, me llegue ese bombón de chocolate blanco y maracuyá que tanto anhelo.

Burnout — experiencia y aprendizaje | sebastiandres

Burnout — experiencia y aprendizaje

¿Cómo reconocer y combatir el agotamiento laboral?

opinión

Disclaimer:

Estos son apuntes personales de mis lecturas sobre el tema. Espero a alguien más le puedan servir.

Mi resumen: no esconderlo, al contrario, hablarlo, recibir opiniones y entender que otras personas también lo sufren o lo han sufrido en el pasado.

El “burnout” — fatiga laboral o agotamiento extremo — se caracteriza por los presentar algunas de las siguientes sensaciones:

  • Sensación permanente de agotamiento, fracaso .
  • Dificultad para concentrarse, baja productividad y baja autoestima.
  • Fantasía de escape e incluso suicidio.
  • Dolor de cabeza, taquicardia o insomnio.
  • Apatía, aburrimiento, impaciencia o irritabilidad.

Primero que nada, hay que desprenderse de la vergüenza y estigma. Tener burnout no es (necesariamente) no querer o no apreciar tu trabajo. Es una condición médica de agotamiento físico y mental. Reconocer el burnout es el primer paso para recuperar el control e interés en tu vida.

Se dice que afecta más a personas de “Tipo A” (competitivos, detallistas y trabajólicos) que de “Tipo B” (relajados y no neuróticos)

Hay ciertas dimensiones a revisar para disminuir el impacto del burnout:

  • Alimentación: reducir toxinas como azúcar, grasas o alcohol. Suele ser muy difícil porque precisamente esas toxinas nos proporcionan placer y suelen ser vías de escape (de corto plazo).
  • Sueño: Necesitas dormir para recuperarte para tener energía y claridad. Es un tema simplemente fisiológico. Hay que planificar horarios para tener un sueño profundo y reparador.
  • Cuerpo: el burnout es una condición que se alimenta de una desconexión con el cuerpo y el mundo físico, por estar demasiado tiempo en tareas de carácter más mental. El ejercicio y meditación son 2 herramientas para conectarte con el cuerpo y con el momento presente. No es necesario destinar grandes tiempos: entrenamientos HIIT y meditaciones de 5 ó10 minutos pueden integrarse a la rutina de manera permanente sin gran impacto.
  • Sintonía y comunicación: reconocer los signos y el nivel de stress, permite buscar apoyo y comunicarlo, sin vergüenza. Ponerle palabras al burnout es simplemente liberador, y proporciona contención y alivio.
  • Mentalidad: mantener una mentalidad positiva sobre nuestra vida y aceptar asumir la responsabilidad personal de la situación, nos puede ayudar .

En mi caso, el burnout viene de poner poca atención a las señales y exceso de pantallas (notebook, smartphone, televisión), potenciado con stress de cuarentena. Para contrarrestarlo, reduje de cuatro a una única taza de café en grano al día, además de asegurarme de dormir al menos 7 o idealmente 8 horas diarias. Volví a establecer rutinas de ejercicio y meditación, y reduje la cantidad de tiempo frente a las pantallas. Dejé de estar disponible a cualquier hora, tratando de privilegiar tareas y reuniones en aquellos temas que me son más interesantes. Y quizás lo más importante, dejé de esconderlo, preocupándome de ver si otras personas pueden también estar sufriéndolo y asumí que la responsabilidad de mi salud es sólo mía y que no puedo esperar mejoras sin hacer cambios.

Algunas lecturas:

  • The Opposite of Burnout — Liz Garrett (85 páginas) corto y preciso. Actividades específicas a realizar, con buenos consejos. Recomendado.
  • Burnout to Breakthrough — Eileen McDargh (115 páginas) Tiene capítulos cortos pero no me gustó tanto. Habían demasiados ejercicios y preguntas, por lo que el flujo de lectura estaba muy interrumpido.
PaaS: Python as a Service | sebastiandres

PaaS: Python as a Service

Entregando python en la nube, más fácil que nunca.

opinión   startup   tecnología

La tecnología cambia y nos trae muchas ventajas. Pero el volumen de conocimiento a manejar se ha vuelto enorme: python, ipython, jupyter notebooks y sus librerías por un lado, y por el otro lado, servidores, kubernetes, y un sin fin de siglas. Aquellos que saben de todo esto pueden sacarles el mejor provecho pero… ¿cómo podemos disponibilizar todas estas tecnologías a un usuario que no sabe ni necesita conocer de todo esto?

¿Existe alguna forma de entregar PaaS: Python as a Service?

Sí. Hay una forma que resulta sencilla tanto para el usuario como para el desarrollador.

¿Qué buscamos para el usuario?

La forma más sencilla para un eventual usuario, es crear una librería permita instalarse con pip:

pip install la_libreria_que_hice_con_mucho_esfuerzo

De esa manera un usuario podría usar la librería con jupyter notebook, sin instalar nada en local, con mybinder o google colab. ¡Hasta podría usarlo desde un tablet o celular!

Un ejemplo sencillo: calculando quién es el próximo en estar de cumpleaños.

Tengo buena memoria para algunas cosas, pero los cumpleaños no se cuentan entre esas cosas. Es un problema familiar, y probablemente a más de alguno le podría servir un recordatorio. Con un código en python muy sencillo podría implementar una función que muestre quienes son las próximos 3 personas en estar de cumpleaños y calculara la edad que van a cumplir. ¿Cómo podría compartir ese código con mi familia?

¡Muy simple! Con MyBinder o Google Colab.

La situación sería tan fácil como se muestra en el siguiente gif.

Es posible configurar MyBinder (pero no en Google Colab) para que la librería ya venga instalada por defecto.

## ¿Qué tiene que hacer el desarrollador?

Hay varias cosas que necesitamos tener para poder proveer un servicio fácil a los usuarios:

  • Librería instalable por pip: requiere definir un archivos setup.py y configurar una estructura de carpetas adecuadamente. El código podrá instalarse desde pypi (oficial, estable) y github (edge).
  • Usar git: permite mantener versiones del código y hacer incrementos de código.
  • Interface simple: Esconder la complejidad del código con orientación a objetos, exponiendo sólo lo estrictamente necesario.
  • Guardar parámetros: cuando todo falla, permite debuggear rápidamente (y reproducir las condiciones de ejecución). En el caso anterior, me sirvió para darme cuenta que Google Colab usa python 3.6.9 y por eso no estaban disponibles ciertas funciones recientes de la librería estándar datetime.
  • Documentación: para usuarios más avanzados y para documentar la librería.

¿Cómo funciona?

Empaqueté todo lo anterior en una librería que sirve como framework de trabajo, que basta con descargar y personalizar. La librería está en github y acá está la documentación.

En resumen, el framework tiene preconfiguradas buenas prácticas, librerías y recursos que permiten cumplir con pip, git, interface simple, documentación y guardar parámetros, al menos de la forma más minimal posible.

La rutina óptima es aburrida | sebastiandres

La rutina óptima es aburrida

Y está sobrevalorada

opinion

Nadie es perfecto. Es difícil ser un ultra-maratonista, fundador de startup, medallista fields y nobel, buen padre y buen esposo. Probablemente también es aburrido, pero estoy muy lejos como para saberlo. Hacer todas las tareas diarias que deberíamos no es fácil, y mantener una rutina es -aceptémoslo de una vez por todas- aburrido. Esto hace que frecuentemente sintamos que no estamos avanzando en la vida.

Una aproximación más realista y efectiva es tener una rotación de rutinas. Ponerse un objetivo y una cierta cantidad de tiempo. Enfocarse y obsesionarse hasta lograr ese único objetivo. Algunas ideas:

  • Aprender portugués en 2 meses.
  • Tomar todos los cursos de Deep Learning que puedas en coursera por 3 meses.
  • Hacer una página web y escribir 10 posts en 6 semanas.
  • Mirar en netflix el top 20 de películas en IMBD en 2 meses.
  • Hacer una rutina de deporte durante 4 semanas. Meditar a las 6 am todos los días por un mes.

Tener una rutina óptima está sobrevalorado. Hacer cambios seguidos es más natural y siempre más entretenido. La clave es hacerlo deliberadamente, y no al azar.

Pycon Colombia 2020 - Las charlas | sebastiandres

Pycon Colombia 2020 - Las charlas

Enlaces a los repositorios y videos.

pycon   charlas

Hace algún tiempo hice un post con los keynotes de la PyCon Colombia 2020. Ahora es el turno de las charlas. Para cada speaker, se pone el enlace al video, a las slides, un repositorio y twitter, si existen.

Viernes 7 Febrero | La mañana

  • Mauricio Collazos - Hablemos de voz : video / slides / repo / twitter
  • Carlos Villavicencio - Type Driven Development: video / slides / repo / twitter
  • Jesús Solano - Movie Genre Classification Using Deep Multimodal Neural Networks: video / slides / repo / twitter
  • Jonathan Vallejo Muñoz - Como migrar mi proyecto de Python 2 a Python 3: video / slides / repo / twitter
  • Jose Benitez - The road so far on building a web platform for drones: video / slides / repo / twitter
  • Javier Vergara - Tumaini: A Deep learning object detection API integrated to an android app, case of study in banana disease detection: video / slides / repo / twitter
  • Flavio Percoco - Inheriting code, and I don’t mean classes: video / slides / repo / twitter &star; RECOMENDADO &star; Jose Zapata - Comprensión de la música con Python, una mirada desde la Física y la Matemática: video / slides / repo / twitter &star; RECOMENDADO - USA JUPYTER LAB Y RISE &star;
  • Milton Lenis - Debugging: A senior’s skill: video / slides / repo* / twitter* &star; RECOMENDADO &star;

Viernes 7 Febrero | La tarde

  • Waldemar Lopez - Representación vectorial de nombres de dominio de Internet mediante técnicas de word embeddings: video/ slides / repo / twitter
  • Kevin Hernández - Y is X but X is not always Y: An introduction to Python internals: video/ slides / repo / twitter &star; RECOMENDADO &star;
  • Oscar Cortez - Modern Python packaging without pain: video/ slides / repo / twitter

Sábado 8 Febrero | La mañana

  • Miguel Cabrera - Time Series Forecasting using Machine Learning: video/ slides / repo / twitter
  • Andrés Torres Cano - Programación en paralelo con Python: sácale jugo a los núcleos de tu computadora: video / slides / repo / twitter
  • Kevin Sarmiento Mendoza - Building a Scalable Food Delivery Service Using ElasticSearch Geo Queries: video / slides / repo / twitter &star; RECOMENDADO &star;
  • Miguel Bolivar Portilla - It takes some engineering to serve 41679 users: video / slides / repo / twitter
  • Nick Sweeting - Archiving the Internet Before it All Rots Away : video / slides / repo / twitter* &star; RECOMENDADO &star;
  • Luis David Camacho Gonzalez - High Performance Pandas 2: Time series data analysis: video / slides / repo / twitter
  • Gustavo Angulo Morales - Antes mi servicio se demoraba 5 seg y ahora 300 miliseg, veamos que es profiling en diferentes capas, Base de datos, aplicación, cache, mobile, para llevar a tus aplicaciones a otro nivel de velocidad: video / slides / repo / twitter
  • Carlos Patiño - Probabilistic Programming: Using Python to Simplify Statistical Inference: video / slides / repo / twitter
  • Camilo Cardona - Desarrollo de Dashboards interactivos en Jupyter para personas desinteresadas en interfaces gráficas: video / slides / repo / twitter &star; RECOMENDADO - USA JUPYTER LAB Y RISE &star; Otra charla de contenido similar
  • Carlos de la Torre (y Rodolfo Edelmann) - Conectando microservicios con Python: video / slides / repo / twitter

Sábado 8 Febrero | La tarde

  • Jesus Anaya Orozco - Prueba tu código con PyTest: video / slides / repo / twitter
  • Ben Dechrai - My voice is my passport. Verify me?: video / slides / repo / twitter
  • Angel Galicia - Procesamiento de datos en sistemas distribuidos con python: video / slides / repo / twitter
  • Felipe dos Santos de Morais - AfroPython: Empowering black people using Python in Brazil: video / slides / repo / twitter
  • Galoget Latorre - Hacking Ético con Python: video / slides / repo / twitter
  • Sebastián Flores - Presentaciones y encuestas interactivas en jupyter notebooks y RISE: video / slides / repo / twitter
  • Maria Remolina - Breaking the Enigma: what if Turing had Python?: video / slides / repo / twitter
  • Manuel Alvarado Cobo - Ocultando datos en datos: video / slides / repo / twitter

Otros

Creo que estas finalmente no se dictaron:

  • José Laica Cornejo - Creando aplicaciones de MicroPython para módulos ESP01 y ESP32: video / slides / repo / twitter
  • Sebastian Arango - Enhancing Data Privacy through Federated (Machine) Learning: video / slides / repo / twitter
  • Kevin Patrón - A Python library for Teaching-Learning-Based Optimization: video / slides / repo / twitter
  • Yilsey Benavides Miranda - Máquinas de aprendizaje para analis de datos geoespaciales en procesos de identificación de cobertura terrestre e índices: video / slides / repo / twitter
  • Andrés Gonzalez - IDK: Connecting APIS, developers, bots and customers with Django, Channels and VueJS: video / slides / repo / twitter
Usando columnas en RISE | sebastiandres

Usando columnas en RISE

¿Cómo poner se pueden usar columnas en RISE?

jupyter   rise

Una de las limitaciones de jupyter notebook para crear diapositivas, es que es posible crear de columnas. Por defecto, el texto ocupa toda la pantalla, como se muestra en la próxima celda.

Lo anterior no es ideal porque sería natural mostrarr el texto y la imagen con columnas.

Sin embargo, podemos usar todas las herramientas de html.

Existen distintas opciones: float: left o display: inline-block.

Veamos las diferencias.

Ejemplo de float: left:

Texto anterior.
<div style="clear: both;">
    <div style="float: left; width: 50%; background-color:#FF0000;">UNO</div>
    <div style="float: left; width: 10%; background-color:#FFFF00;">DOS</div>
    <div style="float: left; width: 30%; background-color:#00FF00;">TRES</div>
</div>
Texto posterior.

Se muestra como:

Notar que:

  • Podemos poner tantas columnas como necesitemos, siempre que sumen menos de 100%. Utilizo una columna interior para separar texto de imagen.
  • El texto "se pega" al div, lo cual se ve bastante feo.
  • La opción de background-color es opcional, acá se usa sólo para mostrar donde termina cada columna.

Ejemplo de display: inline-block:

Texto anterior.
<div style="clear: both;">
    <div style="display: inline-block; width: 50%; background-color:#FF0000;">UNO</div>
    <div style="display: inline-block; width: 10%; background-color:#FFFF00;">DOS</div>
    <div style="display: inline-block; width: 30%; background-color:#00FF00;">TRES</div>
</div>
Texto posterior.

Se muestra como:

Notar que el texto "no se pega" al div como antes.

En base a lo anterior, conviene preferir display: inline-block.

¿Como funciona para texto e imágenes?

Texto anterior.
<div>
    <div style="display: inline-block;  width: 50%; background-color:#FF0000;">UNO</div>
    <div style="display: inline-block; width: 10%; background-color:#FFFF00;">DOS</div>
    <div style="display: inline-block; width: 30%; background-color:#00FF00;">
    <img src="2020-03-19-rise-columns/lore_ipsum.png" alt="LoreIpsum">
    </div>
</div>
Texto posterior.

Ahora el texto quedó alineado abajo. Esto es porque por defecto, la imagen se alinea a texto. Esto se puede combatir con la opción adicional de vertical-align: text-top; en la imagen, como se ve a continuación.

Texto anterior.
<div>
    <div style="display: inline-block;  width: 50%; background-color:#FF0000;">UNO</div>
    <div style="display: inline-block; width: 10%; background-color:#FFFF00;">DOS</div>
    <div style="display: inline-block; vertical-align: text-top; width: 30%; background-color:#00FF00;">
    <img src="2020-03-19-rise-columns/lore_ipsum.png" alt="LoreIpsum">
    </div>
</div>
Texto posterior.

También podemos alinear un video:

Texto anterior.
<div>
    <div style="display: inline-block;  width: 50%; background-color:#FF0000;">UNO</div>
    <div style="display: inline-block; width: 10%; background-color:#FFFF00;">DOS</div>
    <div style="display: inline-block; vertical-align: text-top; width: 30%; background-color:#00FF00;">
    <video src="https://github.com/sebastiandres/blog/blob/master/videos/mov_bbb.mp4?raw=true" controls></video>
    </div>
</div>
Texto posterior.

Podemos juntar todo lo anterior en un contenedor estándar de texto e imagen, donde sólo será necesario actualizar las rutas y los porcentajes de las columnas:

<div>
    <div style="display: inline-block;  width: 40%;">Lorem Ipsum Text</div>
    <div style="display: inline-block; width: 10%;"></div>
    <div style="display: inline-block; vertical-align: text-top; width: 50%;">
    <img src="2020-03-19-rise-columns/lore_ipsum.png" alt="LoreIpsum">
    </div>
</div>

y de texto y video, para reutilizarlo a futuro.

<div>
    <div style="display: inline-block;  width: 40%;">Lorem Ipsum Text</div>
    <div style="display: inline-block; width: 10%;"></div>
    <div style="display: inline-block; vertical-align: text-top; width: 50%;">
    <video src="https://github.com/sebastiandres/blog/blob/master/videos/mov_bbb.mp4?raw=true" controls></video>
    </div>
</div>

Finalmente, entonces podemos obtener el texto e imagen tal como lo queríamos:

Observación:

 ¿Qué más me gustaría?

  • Código: A pesar que puedo poner código en las columnas (código "muerto" pues se puede escribir codigo en el markdown), me gustaría más que la columna fueran una celda de python ejecutable.

Update:

Existe una extensión que permite tener 2 celdas de jupyter notebook lado a lado: split cells.

Blog en github.io | sebastiandres

Blog en github.io

Redirigiendo desde sebastiandres.github.io

fastpages

Aún no termino de traducir los posts “originales” y ya estoy pensando en el próximo post. Es un buen signo de que el nuevo sistema es más fácil de usar.

Al seguir las instrucciones para configurar fastpages, pide que el repositorio no sea “github_username.github.io”. En mi caso y después de algunas pruebas, me incliné por “blog”, por lo que el blog queda en https://sebastiandres.github.io/blog/. Sin embargo, me pareció que rompe la expectativa tradicional, puesto que https://sebastiandres.github.io/ arroja un error (página 404-no encontrada).

Se me ocurrió que una forma fácil de solucionar lo anterior es tener un repositorio “github_username.github.io”, en mi caso sebastiandres.github.io, y que contuviera una página html que redirigiera a https://sebastiandres.github.io/blog/. Un “hack” de 2 minutos y que permite responder a la expectativa, sin mayores dolores de cabeza de configuración o código:

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Refresh" content="0; url=https://sebastiandres.github.io/blog/" />
</head>
<body>
<p>Redirigiendo al <a href="https://sebastiandres.github.io/blog/">blog</a>.</p>
</body>
</html>
Keynotes de la PyCon 2020 Colombia | sebastiandres

Keynotes de la PyCon 2020 Colombia

Videos, slides y resúmenes.

pycon   keynote

“Came for the language, stayed for the community” - Brett Cannon

Todos los keynote speakers recalcaron la importancia del trabajo en equipo y la colaboración de la comunidad de Python, como refleja la frase de Brett Cannon de la PyCon US del 2014. Los principales elementos comunes fueron:

  1. Comunicación, inclusión y mentoring: La comunidad de python es un ejemplo de que la colaboración puede más que la competencia. Andrew mencionó la importancia de una buena comunicación entre equipos de ingeniería. Fernando habló del valor de la comunidad y de haber tenido buenos mentores. Emily mencionó lo importante de tener una comunidad receptiva y que potencie nuevos miembros.
  2. Documentación y respaldos:La documentación es crucial para facilitar la colaboración de las comunidades, y para respaldar hacia el futuro: lo que no se documenta se pierde. Emily mencionó que “Documentar es ahorrar dinero”, y que es un esfuerzo necesario. Esto lo mencionó también Nick Sweeting en una charla: damos por sentado que todo lo que leemos hoy estará disponible en todos lados y en el futuro. Respaldar es importante.
  3. Sobre el fracaso: Todos tenemos nuestros fracasos. Hacerlos visibles es importante para mostrar a otros que es normal, y que forma parte del crecimiento y aprendizaje. Fernando mencionó ejemplos de sus fracasos (desconectar el internet en todo Colombia, un curso práctico de computación fallido). Emily habló de sus inseguridades, la falta de confianza para abordar desafíos y de la necesidad de salir de la zona de confort.

Andrew Godwin @andrewgodwin

The Scientist and the engineer: slides original / slides respaldo

  • TLDR; Los sistemas actuales son demasiado grandes para una sola persona. La comunicación y delegación son parte crucial de la ingeniería.
  • “Computer science is no more about computers than astronomy is about telescopes” — Edsger Dijkstra.
  • “In theory there is no difference between theory and practice. In practice there is” — Benjamin Brewster.
  • “A ship in port is safe, but that’s not what ships are built for”— Grace Hopper
  • The real world is messy. Cosmic ray affects RAM and quantum tunneling affects CPUs. Python is a balanced language to deal with a messy world.
  • Learn when and how to forget. You can’t remember all the details all the time. Besides, it’s inefficient. Abstract - Verify - Forget.
  • Scientists observe and question: They are always asking “why?” and “how?”. Engineers build and invent: they look at a question and think of solutions. Be the scientist and the engineer: Model your systems. Ask the tough questions. Build them for the real world.

Sarah Guido @sarah_guido

Data Science Retrospective: slides original / slides respaldo

  • TLDR; La industria siempre creará hype. Lo que no cambia (ni cambiará) es que los datos nunca serán perfectos.
  • What’s the definition of Data Science? Using data to drive business outcomes! Specialized roles are now required: data engineer, machine learning engineer, business intelligence engineer, data analyst, decision scientist, data science engineer, product scientist and more.
  • Learning data science has evolved. Before: no college programs, few bootcamps, early days for Coursera, Codecademy, etc. Today: Lots of free & open source material, (too) many bootcamps and university programs, not so free resources like Coursera, documentation has improved.
  • Lots of new and cool tools: docker, spark, AWS cloud tools, Zeppelin, Sagemaker, dasboarding tools (Looker, Mode, Periscope, Amplitude). Most tools have a python API! Job offers for data science ask for: Python or R, SQL, basic knowledge of statistics and Machine Learning, data intuition, ability to communicate and to be independent. Communications is a must.
  • Data in the wild is still messy: and that’s not ever going to change.

Wes McKinney @wesmckinn

Python for Data Analysis: Past, Present, and Future: slides original / slides respaldo

  • TLDR; El crecimiento de python se debe a una tormenta perfecta de librerías, y el éxito de pandas a que puede leer csv (entre otras cosas).
  • Pandas first version still at Pypi: https://pypi.org/project/pandas/0.1/.
  • Wes is no longer working on pandas since 2013, don’t insist! Python growth is due to several things, pandas being one of them. There was the need of data wrangling, and there was a “perfect storm” of packages. And packaging of libraries was improved.
  • The success of pandas, mostly due to being able to read csv. Python being readable makes everyone can contribute. The new pandas logo is an example of non cs contributions with huge impact See: “PyData NYC 2013: 10 Things I Hate About pandas”
  • Pandas has taken responsability of too many things. It is more productive to have a common computational framework to . This is why Apache Arrow is “a common standard designed for speed, for data processing libraries”. It should be CPU/GPU friendly, memory map huge datasets, and relocate data structures without serialization.
  • Reflexión personal: en 10 años parecerá natural que exista un estándar para dataframe data. ¿Se imaginan lo complejo e ineficiente que sería si cada lenguaje manejara un estándar distinto de chars, enteros y flotantes?

Ines Montani @_inesmontani

The Future of NLP in Python: slide original / slide respaldo

  • TLDR; Lo más dificil es tener buenos sets de datos. Es necesario crear software que nos ayude con eso.
  • Skills are tree shaped: There’s overlap and branches can grow into empty spaces.
  • spaCy: Open-source library for industrial-strength Natural Language Processing.
  • Prodigy: Annotation tool for creating training data for machine learning models.
  • Thinc: Lightweight deep learning library for composing models with a functional type-checked API
  • Why python? general purpose better than specialized “AI language” & easier for developers. Your team needs specialist, generalists and complementary.
  • Problem 1: Connecting layers in DL is hard. Matrix dimensions must match, and it’s not straightforward. Thic allows to simplify to unblock developer experience to unlock productivity.
  • Problem 2: Dependencies and configuration is a nightmare. You need to close the gap between prototype & production.
  • Problem 3: We needed something so we built it.
  • Problem 4: It all depends on the data. It’s better to pay someone on your team to precisely gather/create the data you need. Move fast and train things. Have several models and choose based on results.

Emily Morehouse @emilyemorehouse

We go further together: slide original / slide respaldo

  • TLDR; nadie es perfecto todo el tiempo, incluso los python core developers tienen dudas y han superpuesto al síndrome del impostor. Todos pueden contribuir, y la documentación está subvalorada.
  • Walrus operator := allows to assign and return value. This was hugely polemic. People are afraid to change. But people is the community.
  • If you’re bored in tech, you’re not working on the right things.
  • Writing documentation is hard! You have the bias of not having fresh eyes.
  • Time is money. Documentation saves time. Ergo, documentation saves money.
  • Mentoring is crucial. Reach out to others, and to overcome impostor syndrome.

Fernando Perez @fperez_org

Jupyter, física y comunidades abiertas: slides original / slides respaldo

  • TLDR; El trabajo en comunidad es muy importante. La comunidad de python y de python científico comparten valores y han aprendido de manera recíproca. Fernando desconectó un cable para prestarlo y dejó sin internet a toda Colombia por algunas horas.
  • La gente se sorprende de la comunidad de python: mucha colaboración, no hay envidias ni competencias, a diferencia de conferencias de otras áreas.
  • IPython partió como un hack de una tarde, y se juntó con otros 2 proyectos que tenía funcionalidades similares (Interactive Python, Lazy Python).
  • Mail original anunciando IPython: link
  • Fue crucial encontrar apoyo y colaboradores en el camino: Eric Jones (Enthought), Scipy, John Hunter (matplotlib), Tarvis Oliphant (numpy — scipy), Wes McKinney (pandas) y muchos más, además del apoyo de la comunidad de python (Guido et al.). La comunidad de python científico persigue los ideales de la ciencia: (1) la búsqueda de conocimiento verificable (2) reproducibilidad (3) esfuerzo colectivo en beneficio de la humanidad.
  • Toda contribución tiene valor, y las necesitamos a todas: diversidad geográfica, cultural, lingüística, etc!
  • ¿Impacto generado? Agujeros negros! LIGO 2015 (Nobel 17), primera imagen de un agujero negro 2019 (Nobel ?).
  • Cool stuff: Pangeo — volúmenes ridículamente grandes de datos geológicos, ICESat (icepyx), simpeg, GeoSci.xyz, JupyterBook, cursos de ciencias de datos en Berkeley +2000 alumnos simultáneos,
  • Hay un libro sobre como usar Jupyter como herramienta pedagógica: libro.
Guía del mal padre y otros | sebastiandres

Guía del mal padre y otros

lectura

Comencé con la Guía del mal padre (1, 2, 3 y 4), una risible y fresca mirada sobre la paternidad, que disfrutamos todos en la casa. Luego descubrí sus cómics sobre sus estancias en Birmania (Crónicas Birmanas), Israel (Crónicas de Jerusalén), Corea del Norte (Pyon Pyan) y China (Crónicas de Shenzen). Logra transmitir muy bien la experiencia de vivir en lugares tan particulares, con sus curiosidades y excentricidades. Me recordó cuando viví fuera de Chile, en Francia y Estados Unidos, llenándome de nostalgia.

Sandman volumen 1 | sebastiandres

Sandman volumen 1

lectura

Había leído todas las historias cuando adolescente, comprándolas en desorden y a diversos precios en las escasas tiendas con cómics de San Antonio en los 90’s, pero al leerlas en orden y con anotaciones encontré una profundidad y coherencia mucho mayor. Es una gran obra de arte. Junto con “El libro del cementerio”, lo mejor de Gaiman. La forma en que se van conectando los personajes y las historias es magistral.

Ready Player 2 | sebastiandres

Ready Player 2

lectura

¡Descubrí que existía una continuación a Ready Player One! Lamentablemente, fue una gran decepción. El libro parte lento, se demora en establecer el conflicto, que resulta poco creíble incluso para este tipo de libro. Además se hacen muchas referencias y explicaciones innecesarias al primer libro. Intenta cubrir referencias pop de los 80 y 90s que no cubrió en el primer libro porque no pasaron el corte para la primera novela: las películas de John Hughes (The Breakfast Club, Ferris Bueller’s Day Off, Pretty in Pink), la música de Prince, el mundo de Tolkien, entre otros.

Ready Player 1 | sebastiandres

Ready Player 1

lectura

Me compré la Oculus Quest 2, y aluciné (y sigo alucinando) tanto con la realidad virtual que quise releer este libro, que había comprado y leído en un viaje anterior a EEUU. Dato freak: se comenta que los empleados de Oculus recibían una copia del libro cuando entraban a trabajar a la empresa. Es un bestseller entretenido, con constantes referencias a los videojuegos y música de los 80 y 90s, y enigmas por resolver. Después mi hijo tomó el libro y no logramos despegarlo hasta que lo terminó en 2 días después (y eso que tiene como 600 páginas).

Percy Jackson (1 y 2) | sebastiandres

Percy Jackson (1 y 2)

lectura

Mi hijo leyó los 5 libros en tiempo récord y me instó a leerlos. Es interesante el concepto de la presencia de dioses, semodioses y héroes en un contexto moderno. Novelas adolescente, lectura liviana pero entretenida. En general 6/10, en el contexto de niños y adolescentes 10/10.

Ocasional Magic | sebastiandres

Ocasional Magic

lectura

Más historias de The Moth, a la altura del otro libro. Historias emocionantes, profundas, todas muy distintas unas de otras.

The monk who sold his ferrari | sebastiandres

The monk who sold his ferrari

lectura

Me llamó la atención el título y leí algunas recomendaciones, por lo que decidí darle una oportunidad. Terrible error, fue una tortura, pero seguí avanzando por si en algún momento llegaba la iluminación y se justificaba el libro. Dicho momento no llegó.

A Smarter Way to Learn JavaScript | sebastiandres

A Smarter Way to Learn JavaScript

lectura

Quería refrescar y profundizar mi conocimiento en javascript sin tener que leer demasiado. Este libro se consumía en capítulos cortos y prácticos, justo lo que necesitaba.

Mindset | sebastiandres

Mindset

lectura

La versión actualizada del 2017 me pareció mucho mejor que el libro original, con más ejemplos y consejos prácticos. Rescato algo que en lecturas anteriores no capturé. Todos tenemos una mezcla de mentalidad fija y de crecimiento, que se manifiestan en distintas oportunidades. Mis principales rasgos fijos a cambiar: aceptar retroalimentación negativa.

The gifts of imperfection | sebastiandres

The gifts of imperfection

lectura

En línea con el libro de “Trillion Dollar Coach”, habla sobre cómo la vulnerabilidad nos conecta como humanos. Los 3 regalos de la imperfección son el coraje, la compasión y la conexión, en una invitación a vivir de manera más auténtica: aceptarnos con nuestras imperfecciones, abrazar nuestros gustos y excentricidades, y dejar de aparentar y creernos el cuento de la madurez. Dos historias me marcaron. La primera: lo importante de no disminuir la importancia de eventos o cosas que sí nos importan para proteger nuestro ego en caso que no resulten, porque tiene como consecuencia que estos mismos eventos no nos alegren o emocionen tanto cuando pasan. La segunda: ser valiente no tiene una única cara. Para una persona extrovertida y osada, ser valiente puede ser no responder un insulto y empezar una pelea. Para una persona introvertida, ser valiente puede ser exactamente lo contrario: responder un insulto y defender su opinión.

Estrategia de Datos | sebastiandres

Estrategia de Datos

lectura

Un libro más bien técnico, sobre distintas estrategias a tomar respecto a los datos de una organización. Interesante, pero de una redacción dificil de seguir.

Trillion Dollar Coach | sebastiandres

Trillion Dollar Coach

lectura

Había escuchado de este libro en podcasts relacionados con productividad y coaching, pero hasta ahora no me había dado el tiempo. Gran pequeña joya de 100 páginas. Lo más destacable: la importancia de relacionarse más allá de lo laboral, de traer lo humano y lo personal al trabajo. Hice un post aparte, para destacar sus principales lecciones y relacionarlas con la metodología SCRUM; tienen muchas cosas en común.

All these wonders | sebastiandres

All these wonders

lectura

Mi hermana me introdujo al mundo de estas breves historias, personales y emotivas. Son perfectamente cortas para leer en cualquier momento, pero profundas y emocionantes para que no sean irrelevantes. Difícil decir cuál me gustó más.

IQ | sebastiandres

IQ

lectura

Una novela policial atípica: el detective es un joven afroamericano sin estudios pero astuto y con mucha calle, que se mueve entre la pobreza y el lujo de Los Ángeles. El caso no es un crimen cometido, sino una sospecha de asesinato por encargo en pleno desarrollo. Diálogos muy divertidos, entretenido y rápido de leer, aunque sin mucho misterio. 100% seguro que lo convertirán en película.

Nudge | sebastiandres

Nudge

lectura

Una propuesta interesante, sobre lo fácil que es realizar pequeñas influencias en las elecciones. Se apoya en la diferencia entre los Econs y Humans. Los Econs son los personajes mitológicos que realizan analizan todos los datos disponibles y realizan elecciones perfectas, mientras que los Humans son personas que actúan como humanos reales: a veces emocional, a veces irracional, a veces intuitivamente. La propuesta es interesante, pero el libro es más largo de lo que debería y demasiados ejemplos son anglocéntricos. A pesar que fue escrito el 2008, algunos ejemplos se sienten un poco desactualizados (Netflix hace rato no es una “mail-order DVD rental company”) 7/10.

Matadero Palma | sebastiandres

Matadero Palma

lectura

Lo compré después que lo recomendaron en un taller de narración policial. No era lo que esperaba, sino que mucho más. El mundo del matadero Franklin en los años 1940, con todos sus personajes memorables. Un verdadero clásico moderno.

Never split the deal | sebastiandres

Never split the deal

lectura

Mezcla de anécdotas de distintas negociaciones y una larga lista de consejos. Muy, pero muy bueno. De esos libros que deseas que alguien te hubiera recomendado antes. No me esperaba que este libro me hiciera pensar, reír y emocionar.

Flying Free | sebastiandres

Flying Free

lectura

Conocí a Cecilia en una charla en la UTFSM, y su sinceridad y humanidad me llamó profundamente la atención. No tenía idea que fue piloto acrobática de aviones, y su historia es increíble. Me devoré el libro y lo he recomendado a varios amigos. Extrañé fue un final más gradioso.

Atomic habits | sebastiandres

Atomic habits

lectura

A pesar que ya había leído varias otros libros y artículos sobre hábitos, este libro me gustó mucho y aprendí más de lo que esperaba. Está bien estructurado, fácil de leer y tiene una mezcla interesante de anécdotas personales y consejos prácticos.

10/10 (en su género).

Elon Musk y Jack Ma | sebastiandres

Elon Musk y Jack Ma

¿Quién tiene la razón en IA y Tech?

opinión   tecnología

En Agosto del 2019 Elon Musk y Jack Ma sostuvieron un panel intercambiando opuestas opiniones en el contexto del World Artificial Intelligence Conference. Los temas que los CEOs de Space X & Tesla y Alibaba&Aliexpress seleccionaron fueron los siguientes: Inteligencia Artificial, Marte, Trabajo y Educación. El panel dura cerca de 46 minutos y fue interesante porque Elon y Jack tienen opiniones bastante distintas y no lo disimularon.

Round 1: Inteligencia Artificial — primer acto (00:00–09:30)

  • Elon Musk: “Ya somos cyborgs, estamos integrados con nuestros computadores y smartphones. Pero el ancho de banda de entrada es demasiado lento”. “Los humanos seremos aburridos y lentos para una Inteligencia Artificial (IA). El lenguaje humano hablado será lentísimo para las IAs, les parecerá como canto de ballenas”. “Hace 40 años, existió el juego pong. Hoy los juegos son foto-realistas con millones de personas jugando en línea simultáneamente. A cualquier tasa de desarrollo que consideres, por extrapolación, la realidad virtual será indistinguible de la realidad. Eso, obien la civilización terminará. Esas son las opciones”. “La velocidad de cambio de la tecnología supera nuestra velocidad de entenderla”. “Los humanos son un gestor de arranque (bootloader) biológico de IA”.
  • Jack Ma: “Las predicciones del futuro son 99.99% incorrectas. El 0.01% de predicciones correctas es por simple azar”. “AI no es una amenaza, los jóvenes son inteligentes y encontrarán soluciones si existen problemas”. “Los intelectuales de academia (college smart) se preocupan, los astutos de la calle (street smart) se adaptan”.
  • Opinión: Lo de que el AI nos encontrará aburridos me recuerda la película Her (altamente recomendable). Por otra parte, es cierto que el desarrollo tecnológico avanza a tasas increíbles. Resulta difícil anticipar los próximos 10 años, pero creo que la realidad virtual será una tecnología mucho más presente de lo que se piensa. La complacencia optimista de Ma no ayuda a solucionar los problemas que ya tenemos con IA hoy en día: seguridad, sesgo, cibervigilancia, entre muchos otros. Necesitamos identificar y reconocer los problemas para solucionarlos, no basta con decir “alguien los solucionará”.

Round 2: Marte (9:30–17:35)

  • Elon Musk: “Necesitamos tomar las acciones necesarias para perdurar la conciencia hacia el futuro”. “Debemos asegurarnos de ser una especie multiplanetaria, es la única opción segura para la humanidad”. “Tenemos la ventana de posibilidad por primera vez, pero no se sabe por cuánto tiempo más”. “Necesitamos menos del 1% en convertirnos en especie multiplanetaria, es equivalente a invertir entre los gastos de cosmética y de salud”.
  • Jack Ma: “Está bien enviar 1 millón de personas a Marte, pero hay 6.7 billones de personas en la tierra. Necesitamos mejorar las cosas acá”.
  • Opinión: En el segundo round, nuevamente Jack Ma cae en una posición un poco conservadora y complaciente. La conquista de la luna produjo en la tierra una serie de avances tecnológicos. Se ha estimado que por cada dólar gastado en la carrera espacial, EEUU recibió 7 a 14 dólares en negocios asociados. Tomando el 10% de esa afirmación, ya es un asunto rentable. Por otra parte, hay un pensamiento que me resulta inquietante. En la tierra existe un equilibrio nuclear entre las grandes potencias debido a que una guerra nuclear aniquila a ambos bandos. ¿Sin embargo, que pasaría entre una guerra nuclear entre la Tierra y Marte? Un planeta podría eliminar completamente la humanidad del otro planeta sin consecuencias.

Round 3: Trabajo (17:35–22:45)

  • Jack Ma: “Deberíamos trabajar entre tres días a la semana, 4 horas por día. La electricidad permitió que usáramos mejor el día. AI permitirá que los humanos hagan las cosas que disfrutan y los caracterizan, como la pintura, el baile, y en general el arte”. “En mi vida he visitado unas 300 ciudades. Mi padre visitó unas 30 ciudades, y mi abuelo unas 3 ciudades”. “Primero, tendremos mucho trabajos. Segundo, no necesitaremos tanto trabajo. Tercero, el problema será que las personas vivirán más y tendrán menos hijos”.
  • Elon Musk: “Tenemos que solucionar el tema de la interface de IA y la mente — neurolink — o nos quedaremos atrás”.
  • Opinión: Esta fue una de las partes del debate que más me dejó pensando. No la parte de trabajar 4x3 horas, que resulta irrisoria viniendo de alguien que ha celebrabado la cultura de 6 días por 12 horas en China (el llamado 6–9–9 por seis días llegando a las 9 am y yéndose a las 9 pm). La parte que encuentro interesante es cómo hoy en día tenemos el privilegio de conocer otras ciudades, países y culturas que sin duda permite combatir la xenofobia y otros males asociados. Existe una curiosidad natural en viajar y conocer el mundo. Existe una crítica que los “millenials viajan al sudeste asiático”. Pero pensándolo bien, es una decisión económicamente muy racional. ¿Dónde es posible obtener las mayores diferencias culturales y aprendizajes, al menor precio posible? Lo cual nos lleva a que parece que hoy en día las mayores divisiones son políticas y no geográficas. Por otra parte, resulta difícil anticipar cómo serán los trabajos del futuro, pero sin duda muchos de los trabajos de hoy en día serán reemplazados. Más aún, hoy ya existen trabajos extremadamente bien pagados que hace 5 ó 10 años hubieran sonado de ciencia ficción: desde ingenieros de Machine Learning hasta Youtubers. Un último pensamiento: hay que tomar extremo cuidado para evitar la concentración de capital y sobre todo de los datos, que cada vez son una fuente de poder. Y como dijo Montesquieu: “el poder corrompe y el poder absoluto, corrompe absolutamente”.

Round 4: Educación (22:45–29:50)

  • Jack Ma: “La forma en que enseñamos educación fue diseñada para el periodo industrial”. “Ya no es importante recordar o calcular: los computadores pueden recordar o calcular más rápido. El humano se caracteriza por ser más creativo y construir cosas”. “Computador tiene un chip, el humano tiene corazón, y es el corazón de donde viene la sabiduría”. “El mayor recurso son las personas y sus cerebros. Tenemos que cambiar la educación para sacar el mayor provecho”.
  • Elon Musk: “Tratar de aprender a predecir o hacer el futuro”. “Evalúa si lo que estás haciendo te permitirá a predecir el futuro mejor”. “Con neurolink podrás aprender algo inmediatamente, como en The Matrix. La educación hoy en día es demasiada demasiada lento”. “Lo más importante es intentarlo, y ajustar en base al error en tus predicciones”.
  • Opinión: Opiniones tibias y palabras comunes. Fue el punto más bajo de la discusión. Y respecto a la educación hay tanto por decir. El futuro estará lleno de nuevas y atractivas opciones. Pero no será una realidad completamente distinta. Coexistirá lo antiguo con lo nuevo, como sucede con los hoteles y airbnb, con los taxis y uber/didi/beat/etc., con la televisión y netflix y youtube. Seguirán existiendo las universidades, institutos y centros profesionales, con la incorporación de una mayor flexibilidad y nuevas tecnologías, pero donde el énfasis estará puesto en carreras tradicionales, la presencialidad y el crear comunidades y redes locales. Por otra parte, co-existirán nuevas instituciones virtuales con un énfasis en micro-aprendizajes, en los últimos descubrimientos y tecnologías, y donde se crearán comunidades globales alrededor de pasiones comunes. Por supuesto que la educación debe cambiar, todo el mundo viene diciendo eso desde hace 50 años. El problema es que mientras el mundo del trabajo no lo exija, esos cambios no se materializarán. En aquellos trabajos donde la educación tradicional no basta, como en Machine Learning y Data Science, la educación alternatiiva o en línea comienza cada vez a tomar mayor relevancia.

Round 5: IA versión 2 (29:45–40:35)

  • Elon Musk: “Definitivamente los computadores pueden hacer cosas más inteligentes que los humanos”. “Hace 22 años, Deep Blue superó a Kasparov. Hace 10 años, AlphaGo superó al campeón de go 4–1. Actualmente AlphaZero le gana invicto a AlphaGo 100–0. Ahora ya no tiene sentido. […] La superioridad de la inteligencia humana cada vez se reduce a un espacio más pequeño”. “Estamos resolviendo problemas cada vez con más grados de libertad. La realidad es el problema con mayor cantidad de grados de libertad. Quizás estamos en una simulación ahora. Existe una broma al respecto. Si estuviéramos en un juego, la evaluación sería: awesome graphics, confusing plot, slow respawn”. “No me preocupa la sobre-población. El mayor problema que enfrenterá la tierra en 20 años es el colapso de la población”.
  • Jack Ma: El computador puede ser astuto (clever), pero el humano es inteligente (smarter)”. “La astucia está guiada por el conocimiento, la inteligencia está guiada por la experiencia”. “Los humanos inventamos al computador. No he visto un computador inventar un humano”. “Sería estúpido correr contra un automóvil. Es lo mismo jugar ajedrez o go contra un computador”. “Los humanos son superiores en creatividad y colaboración”.
  • Opinión: Ma cae de nuevo en los lugares comunes, pero me gusta el ejemplo de correr contra un automóvil. El temor que tenemos hoy de la IA, es el mismo que tenían nuestros padres de las calculadoras que veían inutilizados sus conocimientos de “reglas de cálculo” pero que insistían en la importancia de sacar raíces a mano. Más que nunca, resulta importante saber aprender y adaptarse. Tendremos que redefinir la inteligencia humana constantemente.

Round 6: Life (40:35–46:00)

  • Elon Musk: “Creo que los humanos resolverán los problemas de sustentabilidad del planeta. No es complacencia, debemos hacer algo. Pero podemos hacerlo”. “Con neurolink la edad no importará. Podrás salvar tu estado y restaurarlo, como en un juego. Podemos solucionar el tema del envejecimiento, pero no basta con yoga y alimentación. Es necesario cambiar el ADN. ¿La gente estará de acuerdo con manipular el ADN? Ese es el problema. Es necesario detener el reloj del ADN. ¿Tenemos que cambiarlo? Quizás morir es algo bueno. Es bueno tener un ciclo de vida”
  • Jack Ma: “Las personas inteligentes saben lo que quieren. Las personas sabias saben lo que no quieren”. “Hay que limpiar esta tierra, es un desafío más grande que ir al espacio”. “Está bien hacer errores. Está bien aprender de los errores. Está bien morir”.
  • Opinión: La conversación sobre la vida eterna y guardar la conciencia se asemeja demasiado a la serie “Altered Carbon”. Y tal como en la serie, eso genera una serie de problemas políticos y humanos al generar un estado anti-natural, o al menos, uno al que los seres humanos no se han enfrentado antes. ¿Cómo castigar a un criminal de por vida? ¿Si la persona está en la mente, que diferencia hace cambiar de cuerpo o “envase”? ¿Qué distribución de poder se obtiene con ricos que perpetúan su riqueza durante siglos? Por otra parte, también aparecen las temáticas de la película de ciencia ficción “Ellysium” con separaciones radicales en el acceso a la tecnología y salud dependiendo de las condiciones económicas. ¿Cómo evitar que las brechas que han ido disminuyendo en los últimos años no cambien de rumbo y comiencen a aumentar nuevamente? Probablemente, la respuesta está en la educación y la política. Educación, para adaptarnos a los tiempos futuros, y saber que podemos y tenemos que exigir mejores y más justas leyes. Y buenos políticos, para hacerlas.
Reconociendo nuestros privilegios | sebastiandres

Reconociendo nuestros privilegios

Reflexiones sobre el talento, la suerte y la pobreza.

opinión

Hace poco re-compartí en facebook un video que me pareció interesante. No pensé que recibiría mucha atención, e incluso pensé que podía ser un poco cliché. Tanto así que reconozco incluso que no lo vi completo; al mirarlo nuevamente con mayor detalle me di cuenta que los últimos 40 segundos tienen un texto bíblico. El video es el siguiente:

Carrera por los 100 dólares ¿Cuánto influyen nuestros privilegios?

Uno de mis amigos de facebook respondió con vehemencia, manifestando “lo inútil que eran estos videos que no cambian nada” y que “los ‘buenistas’ han hecho un conjunto de leyes completamente injusto y arbitrario tratando de ‘arreglar’ una supuesta diferencia”. Intercambiamos varios mensajes al respecto, pero el tono de la conversación comenzó a subir y probablemente ambos quedamos con la idea de que el otro no escucha razones. Pero no deja de ser válido el auto-cuestionarse: ¿Existen estas diferencias o privilegios? ¿Porqué son estas diferencias importantes y deberían reconocerse? Me parece que el primer punto es evidente y está fuera de debate. Las diferencias existen debido a las distintas configuraciones familiares, sociales y económicas, y con toda seguridad siempre existirán. Ese no es el punto en cuestión. Respecto al segundo punto, existe un webcomic del ilustrador Toby Morris que también habla sobre los privilegios y que lo ilustra de otra manera. El enlace original está en este link y encontré la versión traducida en este otro sitio. Coloco acá las imágenes para hacer más directa y lineal la lectura:

On a Plate — https://marcianosmx.com/en-bandeja-de-plata-una-historia-sobre-los-privilegios/

El punto central del video y del comic es el mismo. De manera frecuente, quien ha tenido privilegios ignora que los ha tenido, y piensa que todo su éxito ha sido únicamente personal. Esta situación ha sido incluso estudiada, como muestra el siguiente video del investigador y sicólogo Paul Piff:

Paul Pill — ¿El dinero nos hace “malvados”?

Un resumen muy corto del video sería: (1) en un juego de monopoly con condiciones desiguales para los jugadores, los ganadores atribuyen su éxito a condiciones innatas y no a la gran ventaja que arbitrariamente se les ha otorgado, es decir, no reconocen sus privilegios, y (2) en diversos experimentos las poblaciones más ricas tienden a demostrar menos empatía hacia otros. No es que ser rico te obligue a ser “malvado” ni que para ser rico haya que ser “malvado”, sino que el cerebro necesita racionalizar los hechos para poder justificar que exista una diferencia. De manera natural, los humanos buscamos evitar aquello que nos produce alguna emoción conflictiva, y resulta cognitivamente conflictivo admitir que el éxito puede deberse, además de trabajo y esfuerzo, a variables completamente fuera de nuestro control. ¿Es más importante la suerte que el talento innato? Un estudio publicado por 3 italianos en 2018 responde parcialmente la pregunta sobre la importancia de la suerte y el talento. El artículo original de Pluchino, Biondo y Raspisarda es éste, y aunque hay un buen resumen en este artículo, mencionaré los principales puntos a continuación. La idea central del artículo es cuantificar el rol de la suerte y el talento en el éxito de una persona. Para ello hicieron un modelo muy simple en el cual simularon la evolución profesional de 1000 personas durante 40 años. Cada individuo poseía distinto grado de talento natural fijo, y que este talento les permitiría sacar mayor provecho de las oportunidades de suerte en su (simulada) vida. Todos los individuos comenzaban con 10 unidades de éxito pero distinto nivel de talento, y cada 6 meses, los individuos recibían al azar una cierta cantidad de eventos de buena o mala suerte. En caso de buena suerte, su éxito se incrementa proporcionalmente a su talento. En caso de mala suerte, su éxito se dividía a la mitad. ¿Qué pasó al cabo de 40 años? Los individuos que poseían los mayores valores de éxito eran — en promedio — sólo ligeramente más talentosos que una persona promedio. De hecho tener un muy alto talento sólo en un 3% de los casos permitía estar en el grupo de los más exitosos. Raya para la suma: la distribución inicial de talento influía mucho pero mucho menos que la suerte. Sin embargo, resulta natural elevar algunas críticas contra la simpleza del experimento, pues el resultado depende en gran medida de los valores elegidos como condiciones iniciales para el experimento y está lejos de representar la complejidad de la vida humana. El supuesto que el valor inicial de éxito sea el mismo para todos es una simplificación enorme. Todos estamos expuestos a rachas de mala suerte: enfermedades personales o familiares, despidos, accidentes, deudas imprevistas, por citar sólo algunos. Estas situaciones finalmente impidan que las personas puedan ejercer y contribuir con sus talentos a la sociedad. Esto lo expone con gran claridad el historiador Rutger Bregman en el siguiente video:

Rutger Bregman — La pobreza no es falta de carácter.

Rutger Bregman menciona que la pobreza no es una falta de educación, o un defecto de personalidad de ciertas personas. Las condiciones de pobreza producen un efecto similar a consumir alcohol o una noche sin dormir; reducen la capacidad intelectual de manera transitoria. No se menciona porqué, pero imagino que debe estar relacionado a los químicos liberados en condiciones de stress. Adicionalmente, Rutget menciona que la pobreza induce una “mentalidad de escasez” que aniquila la capacidad de planificación a futuro; sólo se atiende a la supervivencia y a lo inmediato. Retomando el video inicial, y para no perder el hilo, la pobreza y los privilegios, son un problema que cuesta caro porque generan una enorme pérdida de potencial humano. ¿Y cómo estamos en Chile hoy en día?

Daniel Matamala en esta columna de La Tercera, relata los resultados de Meneses, Blanco y Paredes. Para ello, usaron bases de datos del Simce, de la PSU y del seguro de cesantía. Y se reproduce lo que de ya sabíamos: el sueldo percibido reproduce la estructura social y no los talentos de las personas. Y resulta difícil decirlo mejor que el periodista, que concluye: «Estas no son anécdotas ni impresiones. Son datos duros de más de 75 mil chilenos, y que probablemente subestiman la realidad, porque se refieren sólo a ingresos del trabajo, y no a otros como acciones, dividendos o herencias, que son mayores entre quienes vienen de un estrato alto.

Estos datos desmienten la idea de una sociedad que dice recompensar el talento y el esfuerzo de sus hijos. En el mercado del trabajo pesan el elitismo disfrazado de “roce social”, la endogamia vestida de “redes” y la discriminación enmascarada como “buena presencia”.»

Los datos dicen que existe una movilidad social mucho más reducida de lo que debería. Donde el talento de muchos se pierde bajo el peso de sus carencias, de falta de condiciones y privilegios, y por no poseer un colchón económica para superponerse a las malas rachas. Y para peor, donde frecuentemente los privilegiados no se reconocen como tal.

Finanzas Modernas | sebastiandres

Finanzas Modernas

Educación y manejo en Web y Apps.

opinión   finanzas

Confieso que no me siento particularmente educado de economía y finanzas como para hacer un post sobre este tema. De hecho, la única asignatura que he reprobado en mi vida fue “Ingeniería Económica”.

Soy el primero en reconocerlo, encuentro que las finanzas son un tema particularmente aburrido. Pero en todo juego es necesario conocer las reglas para saber cuáles estrategias te acercan más a tus metas. Por eso “minimizar” el tiempo dedicado a las finanzas “maximizando” los beneficios es la mejor idea. Conversando con algunos amigos y conocidos — incluso de la banca — me he di cuenta que no todos conocen los distintos recursos que han ido apareciendo respecto a como informarse y manejar finanzas de manera moderna, y que agradecen que les haya contado de estas webs y apps. Este post, al igual que la de Consejos de libros al mejor precio, sirve para facilitar la difusión de estos tips.

Explicado con Manzanitas

Conocí “Con Manzanitas” a través de alguno de sus posts virales en facebook del 2015. Su autor, Matias Godoy, publicaba una serie de posts explicando el caso cascada con manzanitas, las empresas zombies con manzanitas, y mucho otros, y analizaba la actualidad de manera simple y directa. Gracias a sus consejos, estoy revisando las condiciones del crédito hipotecario que tomé hace apenas un año: ¡la tasa ha bajado de 3.2% a 1.7%, y eso se refleja en casi 50.000 pesos menos en contribuciones al mes! Matías publicó hace poco un libro Cómo invertir explicado con Manzanitas donde en 200 páginas explica de manera simple distintos aspectos de la economía y de cómo invertir.

APV

Sin necesidad de meterse en polémicas sobre la larga lista de problemas de las AFP, resulta innegable la necesidad de un ahorro complementario para pasar una jubilosa jubilación. En caso que no lo conozcas, el APV es un ahorro adicional, en el cual el estado complementa el ahorro que tú aportes:

  • En el régimen A, el estado te deposita de vuelta hasta el 15% de lo que ahorres en un año con un tope de 6 UTM (300 lucas al año). Es equivalente a tener un 15% de interés adicionales en la rentabilidad de tu inversión.
  • En el régimen B, el estado te realiza una rebaja de los impuestos, con un tope de 600 UF (16.8 millones). Esto le sirve a las personas que tienen una renta mensual elevada.

¿Cuál régimen te conviene? Depende de cuánto ganes mensualmente y de cuánto estás dispuesto a invertir. La gracia es que siempre puedes retirar el ahorro en APV de manera anticipada, así que puedes crear un colchón económico importante para la jubilación, pero en caso de emergencia también puedes utilizarlo.

MACH

MACH es una cuenta y app respaldada por BCI. No tiene página web, sino que debes configurar todo desde el celular. Es una “monedero digital”, funciona sólo con la plata que has depositado desde tu banco o que te han pagado otras personas; no puedes endeudarte. Sirve para pagar en Chile pero también en el extranjero, y tiene la gracia que no piden antecedentes ni tiene costos asociados. Es increíblemente fácil, en 5 minutos ya tienes la app configurada. Para pagarle o cobrarle a una persona, la buscas entre tus contactos, colocas el monto y listo. Es ideal para pagarse entre amigos para dividir cuentas de restoranes, regalos y viajes, además de pago de cuentas en línea, entre otros. Tiene un sistema de referidos, por lo que si usas el link me darán 1.000 pesos de los cuales puedes cobrarme la mitad mediante la misma aplicación de MACH; así podrás probar inmediatamente cómo funciona. Warning: el botón de “cobrar” de MACH es un poco adictivo.

Fintual

En Fintual no tienen oficinas comerciales físicas, todo se hace en línea. Tampoco tienen una “app”, sino que desde la página web revisas tus inversiones y certificados. Su gran ventaja es que tienen costos muy bajos y una buena rentabilidad. Son los chicos estudiosos-pero-cool de la banca, postearon unas clases de economía en youtube y además tienen un podcast. Sus series de inversión tienen nombres cool (Risky Norris, Moderate Pitt, Conservative Cloney, Very Conservative Streep), y aplican para inversión tradicional y de APV. Me gusta que hacen mucha educación sobre finanzas, de una manera clara. Yo estoy cerrando el APV que había contratado en otro banco y que debido a costos y seguros adicionales, prácticamente no rentó en 3 años, y traspasándolo a fintual.

También tienen un sistema de referidos. Al usar el link me darán el 1% de tu inversión durante el primer año, de lo cual puedes cobrarme la mitad mediante la aplicación de MACH. Así tendrás una rentabilidad adicional del 0.5%. WIN-WIN.

¡Espero que alguno de estos consejos les hayan sido útiles, y si conocen de alguno que consideren importante y no esté acá, ¡no duden en contármelo!

Lo que nos motiva a aprender y trabajar | sebastiandres

Lo que nos motiva a aprender y trabajar

¿Porqué aprendemos fácil algunas cosas y otras nos cuestan tanto?

opinión   educación

Estoy leyendo “Who Own the Learning” de Alan November. La anécdota inicial del libro me encanta. Alan cuenta que tuvo que volver a la escuela, durante el verano, para reprender un alumno que había entrado sin autorización a la sala de computación. Sin embargo, el estudiante no había realizado daños, sino que simplemente había pasado varias horas sentado frente a la pantalla aprendiendo en una profunda concentración. Era una paradoja: ¿tenía que regañar a un alumno por hacer lo que debe realizarse en una escuela, sólo por no respetar las reglas? Este estudiante estaba aprendiendo por cuenta propia, con una alta motivación intrínseca e incluso contra las mismas restricciones que la escuela le imponía. El estudiante mostraba con orgullo el código que había desarrollado, y atribuía su facilidad para aprender porque el computador le entregaba rápidamente retroalimentación sobre cada línea en su código. Creo que a todos nosotros en el mundillo de la informática nos ha pasado un momento similar, el momento en que algo hace click en la cabeza y nos hace gritar eureka, embriagados con el haber aprendido algo por nuestra propia cuenta gracias a la retroalimentación inmediata en la pantalla. Ese estudiante, en unas horas, pudo aprender más por su propia cuenta que en un semestre de clases gracias a su concentración, interés y motivación.

Lo que me ha sorprendido al leer el libro es que los elementos principales de la narrativa son los mismos de un video que había visto hace un tiempo y que me había causado mucho impacto:

Daniel H. Pink — The surprising truth about what motivate us (11 minutos, 2010)

El libro de November, y el video de Daniel Pink, llegan a la misma conclusión: lo que nos motiva como trabajadores y cómo estudiantes el sentido (purpose), la autonomía (autonomy) y la maestría (mastery).

Buscando el Sentido: ¿Cuántas veces no hemos estado en una clase que no tiene ninguna razón, o aprender conceptos, nombres o fechas históricas que sabemos que olvidaremos apenas se realice la prueba? Resulta para todos una pérdida de tiempo; para el docente que observa el desinterés de los estudiantes y de los estudiantes que perciben al docente como una máquina que pasa contenidos sin reflexionar sobre la importancia de éstos. Como en todo trabajo, para lograr una motivación profunda, es necesario que exista un sentido. Alan propone que las tareas y proyectos tengan un alcance global, de manera que no sea algo que las tareas sólo sean entre el alumno y el profesor, y que el contenido de las tareas puedan perdurar en el tiempo y tener un sentido mayor. Por ejemplo, (1) armando videos resumen de los contenidos que estudiantes en otras partes del mundo puedan mirar, (2) creando apuntes por turnos para compartir con los otros compañeros, (3) creando wikis o apuntes de la materia que mejorar año año, o (4) realizando proyectos que beneficien directamente a una comunidad. En el caso de cursos de computación, el tener repositorios de código abierto que los mismos estudiantes puedan actualizar y mirar, incluso si no están tomando formalmente el curso se ha vuelto una práctica común.

Alcanzando la Maestría: las personas buscamos perfeccionarnos para volvernos mejor en lo que hacemos, incluso al costo de accidentes y lesiones. Por ejemplo, practicando instrumentos musicales, trucos de magia, rutas de parkour o complejas piruetas en skate, que no tienen mayor valor práctico que el saber que se ha logrado dominar una habilidad. Sin embargo, para quien ha logrado avanzar en su pericia y habilidad, resulta una increíble (y adictiva) satisfacción. En términos del aprendizaje, resulta importante no pasar el contenido de un curso de manera rígida y no permitiendo a los estudiantes profundizar y alcanzar el nivel de maestría que desean en sus conocimientos. El libro plantea que los niños buscarán alcanzar la maestría de una materia cuando, por ejemplo, deben explicárselo a sus pares en videos o compartiendo sus notas. De alguna manera, se relaciona con los principios de Gamification que se usan para todo tipo de aprendizajes en plataformas y apps, como por ejemplo Duolingo. El deseo de superar “sólo otro nivel” se mantiene independientemente del nivel en el que nos encontremos y hace que siempre busquemos superarnos.

Logrando la Autonomía: es el deseo de dirigir nuestras propias acciones. Tanto como estudiantes como trabajadores, tenemos una idea clara sobre las cosas que deben hacerse, la prioridad que tienen cada una de éstas y el tiempo que será necesario. No existe nada más frustrante que tener una figura externa que dicte paso por paso (y en el orden equivocado) las tareas a realizar, porque ese orden que debiera servir para todos en la práctica no sirve para ninguno. Para que los estudiantes logren autonomía hay que entregarles libertad, lo cual muchas veces resulta atemorizante para los docentes. Sin embargo, los beneficios exceden todo los riesgos. Como profesor, he observado esto de manera directa. Cuando uno entrega una tarea con 10 preguntas a responder, el estudiante sólo explora su capacidad de responder lo que el profesor quiere oír y el estudiante sólo puede como máximo alcanzar el nivel esperado por el profesor. Cuando uno propone que realicen proyectos que les sean de su interés, deben explorar cómo plantear el problema, como resolverlo, de donde obtener información fiable, cómo presentarlo de la mejor manera, y muchas veces, exceden ampliamente el nivel y profundidad que uno como docente se esperaba.

Bonus: Creando este artículo encontré una ted talk de Alan November, donde Alan November cita a Daniel Pink. Así que, efectivamente, ¡había un enlace directo entre ambos!

Alan November — Who Owns the Learning (11 minutos, 2011)

Consejos de libros al mejor precio | sebastiandres

Consejos de libros al mejor precio

¿Libros a buen precio? BP Digital y BookDepository.

consejo   literatura

Para mi sorpresa, hay muchas personas que no conocen los 2 principales sitios que uso para abastecerme de libros a buen precio: BP Digital y BookDepository. ¡Ojalá este post sea útil en difundir esta información!

BP Digital

BP Digital — Biblioteca Pública Digital — permite pedir libros en formato electrónico. El catálogo es bastante amplio y recientemente me sorprendí de encontrar los 3 libros que pensaba comprar para leer en el verano. Por ejemplo, se encuentran las colecciones de Roberto Ampuero, Hernan Rivera Letelier, Isabel Allende, Roberto Bolaño, Phillip K Dick, Isaac Asimov, George R. R. Martin, incluyendo al reciente Yuval Noah Harari. Tienen cerca de 13,000 libros, aunque la mayor parte sólo en español.

  • Pros: es completamente gratis. También me gusta que los libros sean prestados durante un periodo fijo (15 días, + 7 de renovación) porque permite no holgazanear demasiado y terminar la lectura pedida.
  • Contras: Sólo en formato digital y requiere un rut chileno o identificador válido para residentes en Chile. Tienen muy pocos en idioma distinto al español.

Link: http://www.bpdigital.cl/

Consejos:

  1. Leer un libro a la vez, puesto que el periodo de préstamo es corto.
  2. Pedir varios libros al mismo tiempo, y poner en reserva los libros de alta demanda, porque puede demorar en liberarse el deseado.

BookDepository

BookDepository (BD) es una librería electrónica con más de 20 millones de libros y que hace envíos gratis a todo el mundo sin monto mínimo. Eso hace increíblemente más fácil evaluar si conviene comprar un libro o no en comparación con las librerías locales. Por lo que he comparado, BD es hasta un 50% más barato que comprar el libro en Chile, con la ventaja (o desventaja, según se mire) de contar con el libro en principalmente en inglés (o el idioma original). Permite comprar con tarjeta de crédito, o con paypal, y no he tenido ningún problema hasta ahora (+20 libros). Algunos ejemplos:

  • Artemis — Andy Weir. BD: 14.77 USD. Chile: > 30 USD.
  • Surely you’re joking Mr Feynman — Richard Feynman. BD: 14.32 USD. Chile: >25 USD.
  • The man in the high castle — Phillip K. Dick. US$11.55; Chile: >30 USD.
  • Alice’s Adventures in Wonderland — Lewis Carroll. BD: 4.17 USD; Chile: > 10 USD.

Además, permite acceder fácilmente a libros que no están en Chile, como Weapons of Math Destruction de Cathy O’Neil, The Dad Lab de Sergei Urban, o la colección de novelas gráficas Earth Before Us de Abby Howard. Mención especial para los libros de Feynman, que deberían ser lectura obligatoria en todas las carreras STEM: consideraciones profundas sobre la ciencia, la física y la matemática, además de mil situaciones humorísticas. Un espíritu crítico e irreverente.

  • Pros: El precio indicado es el precio final (incluye envío). Se tiene un catálogo muy grande y permite hacer reservas (pre-order). Los libros llegan en perfecto estado y pueden incluso devolverse (aunque nunca he hecho uso de esta funcionalidad).
  • Contras: Se demora entre 2 y 5 semanas en llegar a Chile.

Link: https://www.bookdepository.com/

Consejos:

  1. Revisar bien el idioma del libro. En una oportunidad encargué un libro y aunque el título venía en inglés, el contenido venía en alemán. Al revisar, efectivamente en la página se indicaba que el libro venía en idioma alemán.
  2. Revisar la sección de ofertas (bargains) que tiene algunos libros interesantes a precios inmejorables. Sobretodo para releer a los clásicos en idioma original.
Cocodrilito aprender a ir al baño | sebastiandres

Cocodrilito aprender a ir al baño

Un cuento escrito a 2 manos con mi hijo

escritura

En una pequeña casita, en medio del África, vivían Papá Cocodrilo y Cocodilito. Papá Cocodrilo ya no se acordaba si Cocodrilito tenía 2 ó 3 años, pero sí sabía que ya era hora que fuera sólo al baño. Ya lo habían intentado antes, sin buenos resultados. Pero esta vez si les resultaría. Así que decidieron que Cocodrilito no usaría más pañales. Papá Cocodrilo no quería dejar solito a Cocodrilito en su primer día sin pañales, pero tenía que ir a cazar el almuerzo del día. No tuvo más remedio que decirle: Cocodrilito, si te dan ganas de hacer caca, grita muy fuerte, para que yo te escuche desde lejos. Y Cocodrilito preguntó: ¿Y qué tengo que gritar, papito? Y Papá cocodrilo le dijo: ¡Tienes que gritar, los más fuerte que puedas, caca, papá, caca! Así yo podré venir a ayudarte. Y entonces Papá Cocodrilo, se fue de la casa a cazar.

Cocodrilo llevaba esperando una hora, inmóvil y escondido en el lago. Esperaba que se acercara una cebra sedienta a tomar un sorbo de la refrescante agua. Y estaba a punto de atacarla cuando escucho a Cocodrilito gritar: ¡¡¡CACA, papá, CACA, papá, CACA!!! Y papá Cocodrilo se apuró en llegar a casa. No atacó a la cebra. No tenía tiempo. Y venía tan apurado, que no se dio cuenta de las raíces del árbol y se tropezó. CRASH PUM OUCH AAAAAY!!! A Papá Cocodrilo se le cayeron 2 dientes. Pero se levantó, y siguió corriendo, hasta que llegó cerca a su casa, con una gran sonrisa de cocodrilo pensando que Cocodrilito había aprendido muy rápido y solito ir al baño. Y Cocodrilito le repitió: ¡CACA, papá, CACA, papá, CACA! CACA…HUATES.¡Se me cayeron los cacahuates que estaba comiendo! ¿Quéeeeeeeeee? - preguntó Papá Cocodrilo. Mira papá, se me cayeron al suelo los cacahuates que estaba comiendo- le dijo cocodrilito. Pero, pero, pero - dijo Papá Cocodrilo - yo estaba a punto a punto de atrapar una cebra, y pensé que me llamabas para ir al baño. Nooo, papá, sólo te quería contar de los caca.. huates. Bueno, dijo papá, me voy a seguir cazando nuestro almuerzo. Llámame solo cuando sea necesario ¿bueno? Y se fue nuevamente a cazar.

Cocodrilo ya no quiso cazar en el lago. Muy peligroso, se dijo a sí mismo, y se fue a cazar al tranquilo río que estaba más cerca de la casa. Se escondió entre los matorrales y espero que se acercara una nueva presa. Pasaron los minutos, y los minutos, y muchos minutos más… hasta que por fin, llegó una jirafa. ¡Qué suerte que tengo! - dijo Papá Cocodrilo. Esta presa está mejor que la cebra. Y estaba a punto de saltar sobre el largo cuello de la jirafa cuando escucho a Cocodrilito: ¡¡¡CACA, papá, CACA, papá, CACA!!! Esta vez sí, se dijo Papá Cocodrilo, y nuevamente se apuró en llegar a casa. Venía tan apurado y preocupado, que no se dió cuenta que un puercoespín se había atravesado en su camino. Y por si no fuera poca su mala suerte, no sólo pisó el puercoespín y sus espinas que se le enterraron en los pies, sino que perdió el equilibrio y cayó sobre un cactus. CRASH PUM OUCH AAAAAY!!! Papá Cocodrilo se levantó de un brinco, se sacó cincuenta espinas de los pies, las manos y su hocico, y corrió rápidamente hasta llegar a su casa.

¡¡¡CACA, papá, CACA, papá, CACA!!! ¡Una caca-túa se comió los caca-huates que se me habían caído al piso! Pero, pero, pero - dijo Papá Cocodrilo - ¡yo estaba a punto de atrapar una jirafa para comer! Pensé que esta vez sí que me llamabas para ir al baño. Nooo, papá, sólo te quería contar de la caca.. túa que se comió los caca.. huates. Bueno, dijo Papá Cocodrilo, seguiré cazando. Esta vez sí, llámame sólo cuando sea necesario, ¿vale?

Cocodrilo ya no quería cazar en el lago ni el río. Y se fue a cazar a la cascada. Sabía que había un elefante que siempre iba a tomar agua en la cascada. Papá Cocodrilo subió la ladera y esperó al elefante en la cima de la cascada. Esperó y esperó, esperó y esperó un rato, hasta que al fin, llegó el Elefante. Con su prima Elefanta y su papá Elefantón. Que suerte tengo -dijo Papá Cocodrilo- esto es incluso mejor que una cebra y una jirafa juntas. Cazar tres elefantes en un mismo día, tendremos un banquete. Y se acercaba sigilosamente, cuando de repente escuchó: ¡¡¡CACA, papá, CACA, papá, CACA!!! Y al escuchar a Cocodrilito se distrajo, y se resbaló, y cayó por la cascada. CRASH PUM OUCH AAAAAY!!! Con tan mala suerte, que cayó sobre papá Elefantón, que era famoso por su mal genio y su fuerte patada. PUM OUCH AAAAAY!!!! Un fuerte patadón de Elefantón lo levantó por los aires y llegó directo a su casa. Papá Cocodrilo se levantó, sin 2 dientes, todavía con algunas espinas, todo rasguñado y moreteado, y le preguntó a Cocodrilito: ¿Ahora sí, Cocodrilito? ¿Vamos al baño? No papá, cáca-ras, papá, las caca-túa comió cacahuates y dejó cáca-ras en el piso. ¡¡¿¿QUEEEEE??!! -dijo el papá- estaba a punto de cazar 3 elefantes. Y además, no se dice cácara hijo, se dice, cáscara. Ahhh - dijo cocodrilito - cáscaras.

Papá - dijo cocodrilito - ahora sí necesito ir al baño. ¿A hacer caca? dijo papá. Ay, papá, no seas vulgar, digámosle número dos.

Contenido destacado

Estos son los últimos 3 contenidos destacados: